coyotte508 HF Staff commited on
Commit
85078f5
·
1 Parent(s): 3b164f6

⚗️ Try to write tweets

Browse files
Files changed (3) hide show
  1. package-lock.json +0 -0
  2. package.json +2 -0
  3. server.ts +75 -79
package-lock.json CHANGED
The diff for this file is too large to render. See raw diff
 
package.json CHANGED
@@ -18,6 +18,7 @@
18
  "@types/koa-compress": "^4.0.3",
19
  "@types/koa-morgan": "^1.0.5",
20
  "@types/koa-router": "^7.4.4",
 
21
  "ts-node": "^10.9.1",
22
  "typescript": "^4.9.4"
23
  },
@@ -28,6 +29,7 @@
28
  "koa-compress": "^5.1.0",
29
  "koa-morgan": "^1.0.1",
30
  "koa-router": "^12.0.0",
 
31
  "prettier": "^2.8.2",
32
  "twitter-api-sdk": "^1.2.1"
33
  }
 
18
  "@types/koa-compress": "^4.0.3",
19
  "@types/koa-morgan": "^1.0.5",
20
  "@types/koa-router": "^7.4.4",
21
+ "@types/oauth": "^0.9.1",
22
  "ts-node": "^10.9.1",
23
  "typescript": "^4.9.4"
24
  },
 
29
  "koa-compress": "^5.1.0",
30
  "koa-morgan": "^1.0.1",
31
  "koa-router": "^12.0.0",
32
+ "oauth": "^0.10.0",
33
  "prettier": "^2.8.2",
34
  "twitter-api-sdk": "^1.2.1"
35
  }
server.ts CHANGED
@@ -1,45 +1,25 @@
1
- import Koa from "koa";
2
- import bodyParser from "koa-bodyparser";
3
- import compression from "koa-compress";
4
- import morgan from "koa-morgan";
5
- import Router from "koa-router";
6
- import { inspect } from "util";
7
  import "dotenv/config";
8
- import { Client, auth } from "twitter-api-sdk";
9
 
10
- const port = 7860;
11
 
12
- const app = new Koa();
13
-
14
- app.use(morgan("dev"));
15
- app.use(compression());
16
- app.use(bodyParser());
17
-
18
- const { API_KEY, API_SECRET, BEARER_TOKEN, CLIENT_ID, CLIENT_SECRET, COOKIE } = process.env;
19
-
20
- const router = new Router();
21
-
22
- app.use(router.routes());
23
- app.use(router.allowedMethods());
24
-
25
- // Initialize auth client first
26
- const authClient = new auth.OAuth2User({
27
- client_id: CLIENT_ID as string,
28
- client_secret: CLIENT_SECRET as string,
29
- callback: "https://huggingface-projects-twitter-image-alt-bot.hf.space/callback",
30
- scopes: ["tweet.read", "users.read", "offline.access"],
31
- });
32
 
33
- // Pass auth credentials to the library client
34
- const twitterClient = new Client(authClient);
 
 
 
 
 
 
 
35
 
36
- const BOT_NAME = "AltImageBot1";
37
  const BOT_ID = "1612094318906417152";
38
 
39
- function debug(stuff: any) {
40
- console.log(inspect(stuff, { depth: 20 }));
41
- }
42
-
43
  interface TweetMentions {
44
  data: Array<{ id: string; text: string }>;
45
  meta: {
@@ -71,10 +51,12 @@ async function ff(url: string) {
71
  return await resp.json();
72
  }
73
 
 
 
74
  async function lookupTweets() {
75
  const data: TweetMentions = await ff(`users/${BOT_ID}/mentions`);
76
 
77
- const lookups: TweetLookups = await ff(
78
  `tweets?ids=${data.data
79
  .map((t) => t.id)
80
  .join(
@@ -82,55 +64,69 @@ async function lookupTweets() {
82
  )}&expansions=attachments.media_keys&media.fields=duration_ms,height,media_key,preview_image_url,public_metrics,type,url,width,alt_text`
83
  );
84
 
85
- const tweetWithImage = lookups.data.find((tweet) => tweet.attachments?.media_keys);
86
- const imageUrl = lookups.includes.media.find(
87
- (media) => media.media_key === tweetWithImage?.attachments!.media_keys[0]
88
- )?.url!;
89
-
90
- console.log("imageUrl", imageUrl);
91
-
92
- const imageResp = await fetch(imageUrl);
93
- const contentType = imageResp.headers.get("Content-Type");
94
- const image = await imageResp.arrayBuffer();
95
-
96
- console.log(contentType, image);
97
-
98
- const altText = await fetch("https://olivierdehaene-git-large-coco.hf.space/run/predict", {
99
- method: "POST",
100
- headers: { "Content-Type": "application/json" },
101
- body: JSON.stringify({
102
- data: [`data:${contentType};base64,${Buffer.from(image).toString("base64")}`],
103
- }),
104
- })
105
- .then((r) => r.json())
106
- .then((r) => r.data);
107
-
108
- console.log(altText);
109
-
110
- await twitterClient.tweets.createTweet({ reply: { in_reply_to_tweet_id: tweetWithImage!.id }, text: altText });
111
- }
112
-
113
- async function listen() {
114
- try {
115
- const promise = new Promise<void>((resolve, reject) => {
116
- app.listen(port, "localhost", () => resolve());
117
- app.once("error", (err) => reject(err));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  });
119
 
120
- await promise;
121
-
122
- console.log("app started on port", port);
123
-
124
- process.send?.("ready");
125
- } catch (err) {
126
- console.error(err);
127
  }
128
  }
129
 
130
- listen();
131
-
132
  process.on("unhandledRejection", async (err) => {
133
  console.error("unhandled rejection", err);
134
  });
135
 
136
- lookupTweets();
 
 
 
 
 
 
 
 
 
1
+ import OAuth from "oauth";
 
 
 
 
 
2
  import "dotenv/config";
3
+ import { setTimeout } from "timers/promises";
4
 
5
+ const { API_KEY, API_SECRET, BEARER_TOKEN, ACCESS_TOKEN, ACCESS_TOKEN_SECRET } = process.env;
6
 
7
+ function getAuthHeader(oauth: OAuth.OAuth, url: string) {
8
+ return oauth.authHeader(url, ACCESS_TOKEN as string, ACCESS_TOKEN_SECRET as string, "post");
9
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ const client = new OAuth.OAuth(
12
+ "https://api.twitter.com/oauth/request_token",
13
+ "https://api.twitter.com/oauth/access_token",
14
+ API_KEY as string,
15
+ API_SECRET as string,
16
+ "1.0A",
17
+ null,
18
+ "HMAC-SHA1"
19
+ );
20
 
 
21
  const BOT_ID = "1612094318906417152";
22
 
 
 
 
 
23
  interface TweetMentions {
24
  data: Array<{ id: string; text: string }>;
25
  meta: {
 
51
  return await resp.json();
52
  }
53
 
54
+ let lastMention = "";
55
+
56
  async function lookupTweets() {
57
  const data: TweetMentions = await ff(`users/${BOT_ID}/mentions`);
58
 
59
+ let lookups: TweetLookups = await ff(
60
  `tweets?ids=${data.data
61
  .map((t) => t.id)
62
  .join(
 
64
  )}&expansions=attachments.media_keys&media.fields=duration_ms,height,media_key,preview_image_url,public_metrics,type,url,width,alt_text`
65
  );
66
 
67
+ if (!lastMention) {
68
+ console.log("added mention", lookups.data[0].id);
69
+ lastMention = lookups.data[0].id;
70
+ return;
71
+ }
72
+ const tweets = lookups.data.filter((tweet) => tweet.attachments?.media_keys.length === 1 && tweet.id > lastMention);
73
+ lastMention = lookups.data[0].id;
74
+ console.log(lastMention);
75
+
76
+ for (const tweet of tweets) {
77
+ const imageUrl = lookups.includes.media.find((media) => media.media_key === tweet?.attachments!.media_keys[0])
78
+ ?.url!;
79
+
80
+ console.log("imageUrl", imageUrl);
81
+
82
+ const imageResp = await fetch(imageUrl);
83
+ const contentType = imageResp.headers.get("Content-Type");
84
+ const image = await imageResp.arrayBuffer();
85
+
86
+ console.log(contentType, image);
87
+
88
+ const altText = await fetch("https://olivierdehaene-git-large-coco.hf.space/run/predict", {
89
+ method: "POST",
90
+ headers: { "Content-Type": "application/json" },
91
+ body: JSON.stringify({
92
+ data: [`data:${contentType};base64,${Buffer.from(image).toString("base64")}`],
93
+ }),
94
+ })
95
+ .then((r) => r.json())
96
+ .then((r) => r.data);
97
+
98
+ console.log(altText[0]);
99
+
100
+ const header = getAuthHeader(client, "https://api.twitter.com/2/tweets");
101
+
102
+ const r = await fetch("https://api.twitter.com/2/tweets", {
103
+ headers: {
104
+ Authorization: header,
105
+ "user-agent": "v3CreateTweetJS",
106
+ "content-type": "application/json",
107
+ accept: "application/json",
108
+ },
109
+ body: JSON.stringify({
110
+ text: altText[0],
111
+ reply: { in_reply_to_tweet_id: tweet!.id, conversation_id: tweet!.conversation_id },
112
+ }),
113
+ method: "post",
114
  });
115
 
116
+ console.log("end", await r.text());
 
 
 
 
 
 
117
  }
118
  }
119
 
 
 
120
  process.on("unhandledRejection", async (err) => {
121
  console.error("unhandled rejection", err);
122
  });
123
 
124
+ async function run() {
125
+ while (1) {
126
+ console.log("looking up");
127
+ await lookupTweets();
128
+ await setTimeout(5_000);
129
+ }
130
+ }
131
+
132
+ run();