Skip to content

Instantly share code, notes, and snippets.

@kidager
Last active May 15, 2021 16:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kidager/c39fa858c6aa090e700522b02ec2b6a5 to your computer and use it in GitHub Desktop.
Save kidager/c39fa858c6aa090e700522b02ec2b6a5 to your computer and use it in GitHub Desktop.
look-for-rdv.user.js
// ==UserScript==
// @name Doctolib RDV finder
// @namespace https://jacem.chaieb.me
// @version 1.0.2
// @downloadURL https://gist.githubusercontent.com/kidager/c39fa858c6aa090e700522b02ec2b6a5/raw/look-for-rdv.user.js
// @updateURL https://gist.githubusercontent.com/kidager/c39fa858c6aa090e700522b02ec2b6a5/raw/look-for-rdv.user.js
// @description Try to find a covid vaccination slot on doctolib
// @author Jacem Chaieb <jacem@chaieb.me>
// @match https://www.doctolib.fr/
// @match https://www.doctolib.com/
// @match https://www.doctolib.fr/vaccination-covid-19/*
// @icon https://www.google.com/s2/favicons?domain=doctolib.fr
// @run-at document-idle
// @connect doctolib.fr doctolib.com doctolib.fr code.jquery.com cdnjs.cloudflare.com
// @require https://code.jquery.com/jquery-3.4.1.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/ion-sound/3.0.7/js/ion.sound.min.js
// @grant none
// ==/UserScript==
/* jshint esversion: 6 */
;(function ($, window, document, undefined) {
'use strict';
var pluginName = 'doctolibRdvFinder',
defaults = {
debug: false,
disabled: false,
requests_interval: 5, // interval between requests in seconds
cooldown_interval: 0, // interval between open pages in seconds
url_format: "https://www.doctolib.fr/search_results/{{city_id}}.json?ref_visit_motive_id=6970&ref_visit_motive_ids[]=6970&ref_visit_motive_ids[]=7005&speciality_id=5494&search_result_format=json&force_max_limit=2",
cities: [],
storagePrefix: 'doctolibRdvFinder'
};
function doctolibRdvFinder(element, options) {
this.element = element;
this.options = $.extend({}, defaults, options) ;
this._defaults = defaults;
this._name = pluginName;
this._initiated = false;
this.last_opened_tab = null;
this.cities = [];
this.init();
}
doctolibRdvFinder.prototype = {
init: function () {
if (this.options.disabled || this._initiated) {
return;
}
if (this.options.debug == false) {
console.log = function() {};
}
$.ajaxSetup({
headers: {
'x-csrf-token': $("meta[name=csrf-token]").attr("content"),
}
});
this.storage = window.localStorage;
this.storagePrefix = this.options.storagePrefix + '_';
ion.sound({
sounds: [{name: "bell_ring"}],
volume: 1.0,
path: "https://cdnjs.cloudflare.com/ajax/libs/ion-sound/3.0.7/sounds/",
preload: true
});
this._initiated = true;
this.cities = (this.options.cities.length == 0)
? this.getCitiesListFromStorage()
: this.options.cities;
if (window.location.pathname.indexOf('/vaccination-covid-19/') != -1) {
this.addSearchToList(window.location.pathname.replace('/vaccination-covid-19/', ''));
} else {
this.appendSearchStatus();
this.startSearch();
}
},
saveCitiesListToStorage: function (citiesList) {
this.storage.setItem(this.storagePrefix + 'cities', JSON.stringify(citiesList));
},
getCitiesListFromStorage: function () {
try {
var cities = JSON.parse(this.storage.getItem(this.storagePrefix + 'cities'));
if (cities == null) {
return [];
}
return cities;
} catch (e) {
return [];
}
},
addSearchToList: function (cityName) {
var cities = this.getCitiesListFromStorage();
var element = $('div.results div.dl-search-result').first();
if (null == element) {
return;
}
var element_id = element.attr('id').replace('search-result-', '');
if (0 == element_id.length) {
return;
}
if (cities.find(element => cityName == element.name)) {
// If city exists, update its ID
cities = cities.map(c =>
c.name == cityName
? { name: cityName, city_id: element_id }
: c
);
} else {
// Else add it to the list
cities.push({
city_id: element_id,
name: cityName
});
}
this.saveCitiesListToStorage(cities);
console.log('New city saved');
console.log('Name : ' + cityName);
console.log('ID : ' + element_id);
console.log('Cities array content:', this.getCitiesListFromStorage());
},
startSearch: function () {
if (this.cities.length == 0) {
return;
}
if (this.options.disabled) {
console.log("================= Plugin disabled, shutting down =================");
return;
}
console.log("================= STARTING =================");
var dateToday = new Date();
var dateTomorrow = new Date(dateToday.getFullYear(), dateToday.getMonth(), dateToday.getDate() + 1);
dateToday.setHours(0,0,0,0);
dateTomorrow.setHours(0,0,0,0);
if (this.last_opened_tab !== null) {
var diff = (new Date().getTime() - this.last_opened_tab.getTime()) / 1000;
if (diff < this.options.cooldown_interval) {
console.log("================= COOLDOWN, WAIT "+this.options.cooldown_interval+"s =================");
setTimeout(() => { this.startSearch(); }, this.options.cooldown_interval * 1000);
return;
}
}
this.cities.forEach(city => {
var response = $.get({
async: false,
url: this.options.url_format.replace('{{city_id}}', city.city_id),
success: function (result) {
if (result.isOk == false) alert(result.message);
}
}).responseJSON;
if (response.total == 0) {
console.log("["+ city.name + "/" + city.city_id +"][1] No RDV available !");
return;
}
response.availabilities.forEach(availability => {
if (availability.slots.length == 0) {
console.log("["+ city.name + "/" + city.city_id +"][2] No RDV available !");
return;
}
var date = new Date(availability.date);
date.setHours(0,0,0,0);
if (date.toDateString() == dateToday.toDateString() || date.toDateString() == dateTomorrow.toDateString()) {
// OK
console.log("["+ city.name +"] Opening " + response.search_result.url);
window.open(response.search_result.url, '_blank');
this.last_opened_tab = new Date();
ion.sound.play("bell_ring");
} else {
console.log("["+ city.name + "/" + city.city_id +"][3] No RDV available !");
}
});
});
console.log("================= END, WAIT "+this.options.requests_interval+"s =================");
setTimeout(() => { this.startSearch(); }, this.options.requests_interval * 1000);
},
appendSearchStatus: function () {
var text = (this.cities.length > 0)
? '<span title="Currently searching in '+ this.cities.length +' cities ('+this.cities.map(i => i.name).join(', ')+')" style="color:#fff!important;align-self:center">Searching now for free slots...</span>'
: '<span title="No cities added, go to search to add a city to the list" style="color:#fff!important;align-self:center">No cities in the list</span>';
var button = '<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="32px" height="32px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><g transform="translate(50 50)"><g transform="scale(1)"><g transform="translate(-50 -50)"><g><animateTransform attributeName="transform" type="translate" repeatCount="indefinite" dur="1s" values="-20 -20;20 -20;0 20;-20 -20" keyTimes="0;0.33;0.66;1"></animateTransform><path fill="#63a0d4" d="M44.19 26.158c-4.817 0-9.345 1.876-12.751 5.282c-3.406 3.406-5.282 7.934-5.282 12.751 c0 4.817 1.876 9.345 5.282 12.751c3.406 3.406 7.934 5.282 12.751 5.282s9.345-1.876 12.751-5.282 c3.406-3.406 5.282-7.934 5.282-12.751c0-4.817-1.876-9.345-5.282-12.751C53.536 28.033 49.007 26.158 44.19 26.158z"></path><path fill="#ffffff" d="M78.712 72.492L67.593 61.373l-3.475-3.475c1.621-2.352 2.779-4.926 3.475-7.596c1.044-4.008 1.044-8.23 0-12.238 c-1.048-4.022-3.146-7.827-6.297-10.979C56.572 22.362 50.381 20 44.19 20C38 20 31.809 22.362 27.085 27.085 c-9.447 9.447-9.447 24.763 0 34.21C31.809 66.019 38 68.381 44.19 68.381c4.798 0 9.593-1.425 13.708-4.262l9.695 9.695 l4.899 4.899C73.351 79.571 74.476 80 75.602 80s2.251-0.429 3.11-1.288C80.429 76.994 80.429 74.209 78.712 72.492z M56.942 56.942 c-3.406 3.406-7.934 5.282-12.751 5.282s-9.345-1.876-12.751-5.282c-3.406-3.406-5.282-7.934-5.282-12.751 c0-4.817 1.876-9.345 5.282-12.751c3.406-3.406 7.934-5.282 12.751-5.282c4.817 0 9.345 1.876 12.751 5.282 c3.406 3.406 5.282 7.934 5.282 12.751C62.223 49.007 60.347 53.536 56.942 56.942z"></path></g></g></g></g>';
$('div.logo-doctolib').after($(text));
$('div.logo-doctolib').after($(button));
},
};
$.fn[pluginName] = function ( options ) {
return this.each(function () {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName,
new doctolibRdvFinder(this, options));
}
});
};
$('body').doctolibRdvFinder({
debug: true,
cities: [
// { city_id: "2395056", name: "Asnieres-Sur-Seine"},
// { city_id: "2563981", name: "Courbevoie"},
// { city_id: "2725305", name: "Nanterre"},
// { city_id: "2725305", name: "Bezons"},
// { city_id: "2978937", name: "La Garenne-Colombes"},
// { city_id: "2565198", name: "Argenteuil"},
// { city_id: "2294477", name: "Paris"},
// { city_id: "2564709", name: "Suresnes"},
]
});
})(jQuery, window, document);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment