-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtgbot.deno.ts
145 lines (132 loc) · 3.85 KB
/
tgbot.deno.ts
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
const TOKEN = Deno.env.get("TG_BOT_TOKEN");
const TG_CHANNEL_ID = Deno.env.get("TG_CHANNEL_ID");
const DOMAIN = Deno.env.get("DOMAIN");
const API_KEY = Deno.env.get("API_KEY");
const REGIONAL_CHANNEL_IDS = new Map(
["us-central", "europe-west", "asia-west", "asia-east"].map((reg) => [
reg,
Deno.env.get("TG_CHANNEL_ID_" + reg.replace("-", "_")),
])
);
export const webhookPath = "/tg-webhook";
function genRandomToken(bytes: number) {
return btoa(
String.fromCharCode(...crypto.getRandomValues(new Uint8Array(bytes)))
)
.replaceAll("/", "_")
.replaceAll("+", "-")
.replaceAll("=", "");
}
const webhookUrlToken = genRandomToken(96);
export async function init() {
if (!TOKEN || !DOMAIN || !TG_CHANNEL_ID || !API_KEY) {
throw new Error(
"TG_BOT_TOKEN, TG_CHANNEL_ID, DOMAIN or API_KEY is not set"
);
}
for (const [region, id] of REGIONAL_CHANNEL_IDS.entries()) {
if (!id) {
throw new Error(`TG_CHANNEL_ID_${region} is not set: ${id}`);
}
}
await fetch(`https://api.telegram.org/bot${TOKEN}/setWebhook`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
url: `https://${DOMAIN}${webhookPath}`,
secret_token: webhookUrlToken,
allowed_updates: ["message"],
}),
});
poll().finally(() => Deno.exit(1));
}
let latest: Record<string, number> = JSON.parse(
await Deno.readTextFile("data/latest.json")
);
async function poll() {
while (true) {
await new Promise((r) => setTimeout(r, 1000));
const res = await fetch(
"https://bereal.devin.rest/v1/moments/latest?api_key=" + API_KEY
);
const data: { regions: Record<string, { ts: number }> } = await res.json();
const newLatest = Object.fromEntries(
Object.entries(data.regions).map(([reg, { ts }]) => [reg, ts * 1000])
);
for (const [reg, ts] of Object.entries(newLatest)) {
if (ts > latest[reg]) {
await fetch(`https://api.telegram.org/bot${TOKEN}/sendMessage`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
chat_id: TG_CHANNEL_ID,
text: `[${reg}] Time to BeReal!`,
}),
});
const regionalChannel = REGIONAL_CHANNEL_IDS.get(reg);
if (regionalChannel) {
await fetch(`https://api.telegram.org/bot${TOKEN}/sendMessage`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
chat_id: regionalChannel,
text: `Time to BeReal!`,
}),
});
}
}
}
latest = newLatest;
await Deno.writeTextFile("data/latest.json", JSON.stringify(latest));
}
}
export async function handleRequest(e: Deno.RequestEvent) {
if (
e.request.method.toUpperCase() !== "POST" ||
e.request.headers.get("X-Telegram-Bot-Api-Secret-Token") !== webhookUrlToken
) {
await e.respondWith(
new Response("You shall not pass", {
status: 401,
headers: {
"Content-Type": "text/plain",
},
})
);
return;
}
const data = await e.request.json();
await Promise.all([
e.respondWith(
new Response("processing", {
status: 200,
headers: {
"Content-Type": "text/plain",
},
})
),
processTgUpdate(data),
]);
}
async function processTgUpdate(data: any) {
console.log(data.message);
if (data.message.chat.id.toString() !== TG_CHANNEL_ID) {
await fetch(`https://api.telegram.org/bot${TOKEN}/sendMessage`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
chat_id: data.message.chat.id,
text: "Subscribe to @bereal_notifications",
}),
});
return;
}
}