forked from PLhery/chronodose_twitterbot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chronobot.ts
148 lines (126 loc) · 4.45 KB
/
chronobot.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
146
147
148
import axios from 'axios';
import TwitterApi from 'twitter-api-v2';
import StaticMaps from 'staticmaps';
import dotenv from 'dotenv';
dotenv.config();
import dayjs from 'dayjs';
import calendar from 'dayjs/plugin/calendar';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(calendar);
dayjs.extend(utc);
dayjs.extend(timezone);
const twitterClient = new TwitterApi({
appKey: process.env.APP_KEY!,
appSecret: process.env.APP_SECRET!,
accessToken: process.env.ACCESS_TOKEN!,
accessSecret: process.env.ACCESS_SECRET!,
});
if (!process.env.DEPARTMENTS_TO_CHECK) {
console.error('please set the DEPARTMENTS_TO_CHECK env variable');
process.exit(0);
}
const DEPARTMENTS_TO_CHECK = process.env.DEPARTMENTS_TO_CHECK!.split(',').map(Number);
const CHECK_INTERVAL_SEC = Number(process.env.CHECK_INTERVAL_SEC) || 60; // check every X seconds
const MIN_DOSES = Number(process.env.MIN_DOSES) || 0; // don't tweet if less than MIN_DOSES are available, because it's probably already too late
const TIMEZONE = process.env.TIMEZONE || 'Europe/Paris';
// partial data
interface viteMaDoseData {
centres_disponibles: Array<{
nom: string;
url: string;
location: {
longitude: number;
latitude: number;
city: string;
}
metadata: {
address: string;
}
appointment_schedules: Array<{
name: string;
from: string;
to: string;
total: number;
}>;
prochain_rdv: string;
vaccine_type?: string[];
}>
}
// avoid tweeting twice the same message (using a specified ID)
const alreadyTweeted = new Set<string>();
async function checkDepartment(department: number) {
console.log(`fetching db ${department}...`);
const { data }: { data: viteMaDoseData } =
await axios.get(`https://vitemadose.gitlab.io/vitemadose/${addZero(department)}.json`);
console.log(`fetched db ${department}`);
const promises = data.centres_disponibles
// .filter(centre => centre.vaccine_type?.includes('Pfizer-BioNTech') || centre.vaccine_type?.includes('Moderna'))
// .filter(centre => (new Date(centre.prochain_rdv).getTime() - Date.now()) < 24 * 60 * 60 * 1000)
.filter(centre => centre.appointment_schedules
.some(schedule => schedule.name === 'chronodose' && schedule.total > 0)
)
.map(async (centre) => {
// count the number of doses
const nbDoses = centre
.appointment_schedules
.filter(schedule => schedule.name === 'chronodose')
.reduce((nb, schedule) => nb + schedule.total, 0);
if (nbDoses < MIN_DOSES) {
return;
}
// don't tweet twice the same info
const id = `${centre.url} - ${centre.prochain_rdv} - ${nbDoses}`
if (alreadyTweeted.has(id)) {
return;
}
alreadyTweeted.add(id);
const calendarDate = dayjs(centre.prochain_rdv)
.tz(TIMEZONE)
.calendar(dayjs(),
{
sameDay: '[aujourd\'hui à] H:mm',
nextDay: '[demain à] H:mm',
sameElse: 'le DD/MM/YYYY à H:mm',
}
);
const intro = (nbDoses == 1) ?
`${nbDoses} dose est disponible ${calendarDate}` :
`${nbDoses} doses sont disponibles ${calendarDate}`;
const message =
`${intro}\n` +
`à ${centre.nom} (${centre.vaccine_type})\n` +
`${centre.url}\n` +
`${centre.metadata.address}`;
console.log(message);
console.log('generating the map...');
// generate the map image before tweeting...
const map = new StaticMaps({
width: 600,
height: 400
});
map.addMarker({
coord: [centre.location.longitude, centre.location.latitude],
img: 'marker.png',
height: 40,
width: 40,
});
await map.render(undefined, 11);
// tweet
console.log('uploading the media...');
const mediaId = await twitterClient.v1.uploadMedia(await map.image.buffer(), { type: 'png' });
console.log('tweeting...')
await twitterClient.v1.tweet(message, { media_ids: mediaId });
});
await Promise.all(promises)
.catch(err => console.error(err));
}
function addZero(department: number) {return (department < 10 ? `0${department}` : department)}
function checkDepartments(departments: number[]) {
return Promise.all(
departments
.map(department => checkDepartment(department)),
)
}
checkDepartments(DEPARTMENTS_TO_CHECK);
setInterval(() => checkDepartments(DEPARTMENTS_TO_CHECK), CHECK_INTERVAL_SEC * 1000)