Last active
May 15, 2021 16:55
-
-
Save kidager/c39fa858c6aa090e700522b02ec2b6a5 to your computer and use it in GitHub Desktop.
look-for-rdv.user.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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 <[email protected]> | |
// @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