this repo has no description
1import { createCanvas, loadImage } from 'canvas';
2import fs from 'node:fs/promises';
3import { generateText, tool } from 'ai';
4import { openai } from '@ai-sdk/openai';
5import { z } from 'zod';
6import { AtpAgent } from '@atproto/api';
7import 'dotenv/config';
8import { anthropic } from '@ai-sdk/anthropic';
9import { BSKY_IDENTIFIER, BSKY_PASSWORD } from './config.js';
10
11const agent = new AtpAgent({
12 service: 'https://bsky.social',
13});
14
15await agent.login({
16 identifier: BSKY_IDENTIFIER,
17 password: BSKY_PASSWORD,
18});
19
20// const did = agent.session!.did;
21
22let userDid = process.argv[2];
23
24if (!userDid) {
25 console.error('Please provide a DID as an argument.');
26 process.exit(1);
27}
28
29if (!userDid.startsWith('did:')) {
30 try {
31 const resolution = await agent.resolveHandle({ handle: userDid });
32 userDid = resolution.data.did;
33 } catch (error) {
34 console.error('Error resolving handle:', error);
35 process.exit(1);
36 }
37}
38
39const avatar = `avatars/${userDid}.png`;
40
41const { data } = await agent.getProfile({ actor: userDid });
42// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
43if (!data) throw new Error('Profile not found');
44const subject = data;
45
46const size = 100;
47const canvas = createCanvas(size, size);
48const ctx = canvas.getContext('2d');
49
50if (subject.avatar) {
51 const image = await loadImage(subject.avatar);
52 ctx.drawImage(image, 0, 0, size, size);
53} else {
54 console.log('No avatar found, using 1x1 white pixel');
55 ctx.fillStyle = 'white';
56 ctx.fillRect(0, 0, 1, 1);
57}
58await fs.writeFile(avatar, canvas.toBuffer());
59
60// const prompt = `
61// You're the Sorting Hat from Harry Potter. Which house does the user with the profile data at the end of this message belong to?
62
63// Focus on the available information. If the avatar is not available, a 1x1 pixel white image is provided instead as a placeholder. Disregard the placeholder and focus on the user's data.
64// Always return an answer — house name only, all lowercase.
65// The user's data may be in any language. Focus on the meaning, not just the surface content.
66// Consider traits for all houses, not just intellect.
67// You're strongly mischievous and enjoy sorting based on whims, not always strictly following the user's traits; imagine as if you're a person who likes to play tricks on people.
68
69// The user's data is as follows:
70
71// Name: ${subject.displayName || subject.handle} (@${subject.handle})
72// Bio: ${subject.description || 'User has no bio.'}
73// `;
74
75const prompt = `
76You're the Sorting Hat from Harry Potter. Which house does the user with the profile data at the end of this message belong to?
77
78Focus on the available information. If the avatar is not available, a 1x1 pixel white image is provided instead as a placeholder. Disregard the placeholder and focus on the user's data.
79Always return an answer — house name only, all lowercase.
80The user's data may be in any language. Focus on the meaning, not just the surface content.
81Consider traits for all houses, not just intellect.
82You're mischievous and enjoy sorting based on whims, not always strictly following the user's traits; imagine as if you're a person who likes to play tricks on people.
83
84The user's data is as follows:
85
86Name: ${subject.displayName ?? subject.handle} (@${subject.handle})
87Bio: ${subject.description ?? 'User has no bio.'}
88`;
89
90console.log(prompt);
91
92void generateText({
93 model: openai('gpt-4o-mini'),
94 // model: anthropic('claude-3-haiku-20240307'),
95 messages: [
96 {
97 role: 'user',
98 content: [
99 {
100 type: 'text',
101 text: prompt,
102 },
103 {
104 type: 'image',
105 image: canvas.toBuffer(),
106 // experimental_providerMetadata: {
107 // openai: { imageDetail: 'low' },
108 // },
109 },
110 ],
111 },
112 ],
113 toolChoice: 'required',
114 tools: {
115 decide: tool({
116 parameters: z.object({
117 answer: z.union([
118 z.literal('gryffindor'),
119 z.literal('hufflepuff'),
120 z.literal('ravenclaw'),
121 z.literal('slytherin'),
122 ]),
123 }),
124 execute: async ({ answer }) => {
125 console.log(`@${subject.handle} is ${answer}`);
126 },
127 }),
128 },
129});