File size: 4,317 Bytes
85078f5 31d882e 85078f5 fc497aa 31d882e 85078f5 31d882e 85078f5 e04cf41 85078f5 e04cf41 987f2db e04cf41 987f2db 2b4727d 9920173 987f2db 9920173 987f2db e04cf41 987f2db c7cf68e 85078f5 987f2db 58ea57b c7cf68e 58ea57b 987f2db a0fe71c 85078f5 58ea57b 9920173 987f2db c7cf68e 58ea57b 85078f5 fb23f68 c7cf68e 85078f5 31d882e 58ea57b 31d882e e04cf41 99fbfe2 85078f5 e91bcc6 85078f5 99fbfe2 daf0a04 fc497aa |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
import OAuth from "oauth";
import "dotenv/config";
import { setTimeout } from "timers/promises";
import Koa from "koa";
const { API_KEY, API_SECRET, BEARER_TOKEN, ACCESS_TOKEN, ACCESS_TOKEN_SECRET } = process.env;
function getAuthHeader(oauth: OAuth.OAuth, url: string) {
return oauth.authHeader(url, ACCESS_TOKEN as string, ACCESS_TOKEN_SECRET as string, "post");
}
const client = new OAuth.OAuth(
"https://api.twitter.com/oauth/request_token",
"https://api.twitter.com/oauth/access_token",
API_KEY as string,
API_SECRET as string,
"1.0A",
null,
"HMAC-SHA1"
);
const BOT_ID = "1612094318906417152";
interface TweetMentions {
data: Array<{ id: string; text: string }>;
meta: {
result_count: number;
newest_id: string;
oldest_id: string;
};
}
interface TweetLookups {
data: Array<{
id: string;
conversation_id: "string";
text: string;
created_at: string;
attachments?: { media_keys: string[] };
}>;
includes: { media: Array<{ media_key: string; url: string }> };
}
async function ff(url: string) {
const resp = await fetch(`https://api.twitter.com/2/${url}`, {
headers: { Authorization: `Bearer ${BEARER_TOKEN}` },
});
if (resp.status !== 200) {
throw new Error("invalid status: " + resp.status + "- " + (await resp.text()));
}
return await resp.json();
}
let lastCreatedAt = "";
let lastMentionedTweet = "";
async function lookupTweets() {
const data: TweetMentions = await ff(
`users/${BOT_ID}/mentions?${
lastCreatedAt && `start_time=${new Date(new Date(lastCreatedAt).getTime() + 1).toJSON()}`
}`
);
if (!data.data || !data.data.length) {
return;
}
let lookups: TweetLookups = await ff(
`tweets?ids=${data.data
.map((t) => t.id)
.join(
","
)}&tweet.fields=created_at&expansions=attachments.media_keys&media.fields=duration_ms,height,media_key,preview_image_url,public_metrics,type,url,width,alt_text`
);
if (!lastCreatedAt) {
console.log("added mention", lookups.data[0].created_at, lookups.data[0].id);
lastCreatedAt = lookups.data[0].created_at;
lastMentionedTweet = lookups.data[0].id;
return lookupTweets();
}
const tweets = lookups.data.filter(
(tweet) => tweet.attachments?.media_keys.length === 1 && tweet.id > lastMentionedTweet
);
if (tweets.length) {
lastCreatedAt = lookups.data[0].created_at;
lastMentionedTweet = lookups.data[0].id;
}
console.log(lastCreatedAt);
for (const tweet of tweets) {
const imageUrl = lookups.includes.media.find((media) => media.media_key === tweet?.attachments!.media_keys[0])
?.url!;
console.log("imageUrl", imageUrl);
const imageResp = await fetch(imageUrl);
const contentType = imageResp.headers.get("Content-Type");
const image = await imageResp.arrayBuffer();
console.log(contentType, image);
const altText = await fetch("https://olivierdehaene-git-large-coco.hf.space/run/predict", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
data: [`data:${contentType};base64,${Buffer.from(image).toString("base64")}`],
}),
})
.then((r) => r.json())
.then((r) => r.data);
console.log(altText[0]);
const header = getAuthHeader(client, "https://api.twitter.com/2/tweets");
const r = await fetch("https://api.twitter.com/2/tweets", {
headers: {
Authorization: header,
"user-agent": "v3CreateTweetJS",
"content-type": "application/json",
accept: "application/json",
},
body: JSON.stringify({
text: altText[0],
reply: { in_reply_to_tweet_id: tweet!.id, conversation_id: tweet!.conversation_id },
}),
method: "post",
});
try {
console.log("end", await r.json());
} catch {}
}
}
process.on("unhandledRejection", async (err) => {
console.error("unhandled rejection", err);
});
process.on("uncaughtException", console.error);
async function run() {
while (1) {
console.log("looking up");
try {
await lookupTweets();
} catch (err) {
console.error(err);
}
await setTimeout(5_000);
}
}
run();
const SPACE_ID = process.env.SPACE_ID || "huggingface-projects/twitter-alt-image-bot";
// Keep space alive
// setInterval(() => {
// fetch(`https://${SPACE_ID.replace("/", "-")}.hf.space`);
// }, 24 * 3600 * 1000);
const app = new Koa();
app.use((ctx) => {
ctx.body = "Check out the README!";
});
app.listen(7860);
|