birthday.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. registerPlugin({
  2. name: 'Birthday Script',
  3. version: '1.0',
  4. description: 'create birthday notifications',
  5. author: 'mcj201',
  6. vars: [
  7. {
  8. name: 'message',
  9. title: 'The message that should be displayed. (%n = nickname, %b = list of birthdays)',
  10. type: 'multiline'
  11. },
  12. {
  13. name: 'type',
  14. title: 'Message-Type',
  15. type: 'select',
  16. options: [
  17. 'Private chat',
  18. 'Poke'
  19. ]
  20. },
  21. {
  22. name: 'nDays',
  23. title: 'send the notification upto this amount of days after birthday',
  24. type: 'number'
  25. },
  26. {
  27. name: 'serverGroup',
  28. title: 'Server group name/id for birthdays',
  29. type: 'string'
  30. },
  31. {
  32. name: 'davUrl',
  33. title: 'CardDAV URL e.g. https://example.com/dav.php',
  34. type: 'string'
  35. },
  36. {
  37. name: 'davUsername',
  38. title: 'CardDAV username',
  39. type: 'string'
  40. },
  41. {
  42. name: 'davPassword',
  43. title: 'CardDAV password',
  44. type: 'password'
  45. },
  46. {
  47. name: 'davAddressBook',
  48. title: 'CardDAV address book',
  49. type: 'text'
  50. }
  51. ],
  52. autorun: true
  53. }, function(sinusbot, config, meta) {
  54. const event = require('event')
  55. const engine = require('engine')
  56. const backend = require('backend')
  57. const format = require('format')
  58. var store = require('store');
  59. engine.log(`Loaded ${meta.name} v${meta.version} by ${meta.author}.`)
  60. event.on('load', () => {
  61. const command = require('command');
  62. if (!command) {
  63. engine.log('command.js library not found! Please download command.js and enable it to be able use this script!');
  64. return;
  65. }
  66. var xhr = new dav.transport.Basic(
  67. new dav.Credentials({
  68. username: config.davUsername,
  69. password: config.davPassword
  70. })
  71. );
  72. let bDays = store.get('birthdays') || {};
  73. let notifs = store.get('birthday_notifications') || {};
  74. let davClient = new dav.Client(xhr);
  75. let usedAddressBook = null;
  76. davClient.createAccount({
  77. server: config.davUrl,
  78. accountType: 'carddav'
  79. }).then(function(account) {
  80. let addressBook = null;
  81. account.addressBooks.forEach(function(ab) {
  82. console.log('Found address book name ' + ab.displayName);
  83. addressBook = ab;
  84. if(ab.displayName === config.davAddressBook) return;
  85. });
  86. usedAddressBook = addressBook;
  87. });
  88. setInterval(updateServerGroups, 1000 * 60);
  89. setInterval(syncDavAddressBook, 1000 * 60);
  90. event.on('clientMove', ({ client, fromChannel }) => {
  91. const avail = getNotifications(client, 30);
  92. if (avail.length < 1)
  93. return;
  94. let msgs = []
  95. for(const uid of avail) {
  96. msgs.push(`${getName(uid)}: ${formatDate(getBday(uid))}`);
  97. }
  98. const msg = config.message.replace('%n', client.name()).replace('%b', msgs.join('\r\n'))
  99. if (!fromChannel) {
  100. if (config.type == '0') {
  101. client.chat(msg)
  102. } else {
  103. client.poke(msg)
  104. }
  105. updateServerGroups();
  106. }
  107. })
  108. command.createCommand('birthdays')
  109. .help('Show user birthdays')
  110. .manual('Show user birthdays from DB.')
  111. .exec((client, args, reply, ev) => {
  112. let msgs = ["List of saved birthdays:"];
  113. for(const uid in bDays) {
  114. msgs.push(`${getName(uid)}: ${formatDate(getBday(uid))}`);
  115. }
  116. reply(msgs.join('\r\n'));
  117. });
  118. command.createCommand('birthday')
  119. .addArgument(command.createArgument('string').setName('date'))
  120. .help('Set user birthdays')
  121. .manual('Save user birthdays to DB.')
  122. .exec((client, args, reply, ev) => {
  123. var date = args.date.split('.');
  124. if(date.length >= 2) {
  125. let m = date[0];
  126. date[0] = date[1];
  127. date[1] = m;
  128. }
  129. date = new Date(date);
  130. if(args.date === "") {
  131. let date = getBday(ev.client.uid());
  132. if(date)
  133. reply(`Your birthday is ${formatDate(date)}.`);
  134. else
  135. reply(`Set your birthday first! e.g. !birthday 24.12.`);
  136. } else if(!isNaN(date)) {
  137. setBday(ev.client, date);
  138. reply(`Your birthday was set to ${formatDate(date)}.`);
  139. } else {
  140. setBday(ev.client, date);
  141. reply(`Your birthday has been cleared.`);
  142. }
  143. });
  144. function setBday(client, date) {
  145. if(isNaN(date) && bDays[client.uid()]) {
  146. delete bDays[client.uid()];
  147. } else if(!isNaN(date)) {
  148. bDays[client.uid()] = [client.name(), date, new Date()];
  149. }
  150. store.set('birthdays', bDays);
  151. }
  152. function getBday(uid) {
  153. if(!bDays[uid])
  154. return undefined;
  155. if(bDays[uid][1])
  156. return new Date(bDays[uid][1]);
  157. else
  158. return undefined;
  159. }
  160. function getName(uid) {
  161. return bDays[uid][0];
  162. }
  163. function getNotifications(client, nDays = 30) {
  164. const start = new Date();
  165. start.setDate(start.getDate()-nDays);
  166. const now = new Date();
  167. let sentNotifs = notifs[client.uid()] || {};
  168. let avail = [];
  169. for(const uid in bDays) {
  170. let bDay = new Date(bDays[uid][1]);
  171. bDay.setFullYear((new Date()).getFullYear());
  172. let lastNotif = new Date(sentNotifs[uid]);
  173. if(bDay >= start && bDay <= now && (isNaN(lastNotif) || lastNotif < start)) {
  174. avail.push(uid);
  175. sentNotifs[uid] = now;
  176. }
  177. }
  178. notifs[client.uid()] = sentNotifs;
  179. store.set('birthday_notifications', notifs);
  180. return avail;
  181. }
  182. function formatDate(dt) {
  183. if(dt)
  184. return `${dt.getDate()}.${dt.getMonth()+1}.`;
  185. else
  186. return 'invalid date';
  187. }
  188. function updateServerGroups() {
  189. if(config.serverGroup === "")
  190. return;
  191. const now = new Date();
  192. for(const client of backend.getClients()) {
  193. if(bDays[client.uid()]) {
  194. const bDay = new Date(bDays[client.uid()][1]);
  195. let hasGroup = false;
  196. for(const group of client.getServerGroups()) {
  197. hasGroup |= group.name() === config.serverGroup || group.id() == config.serverGroup;
  198. }
  199. if(bDay.getDate() === now.getDate() && bDay.getMonth() === now.getMonth()) {
  200. if(!hasGroup) client.addToServerGroup(config.serverGroup);
  201. } else {
  202. if(hasGroup) client.removeFromServerGroup(config.serverGroup);
  203. }
  204. }
  205. }
  206. }
  207. function syncDavAddressBook() {
  208. if(!usedAddressBook)
  209. return;
  210. davClient.syncAddressBook(usedAddressBook).then(function(x) {
  211. let remoteBDays = {};
  212. let hasUpdates = false;
  213. for(const vcard of x.objects) {
  214. VCF.parse(vcard.addressData, function(card) {
  215. if(!isnan(card.bday) && card.note && card.note.length > 0) {
  216. const uid = card.note[0]
  217. remoteBDays[uid] = [card.fn, card.bday, card.rev];
  218. if(!bDays[uid]) {
  219. //create local
  220. engine.log('new birthday from dav')
  221. bDays[uid] = remoteBDays[uid];
  222. } else if(bDays[uid].count < 3 || bDays[uid][2] < card.rev) {
  223. //pull from dav
  224. engine.log('updated birthday from dav')
  225. bDays[uid] = remoteBDays[uid];
  226. hasUpdates = true;
  227. } else if(bDays[uid][2] > card.rev) {
  228. //push to dav
  229. engine.log('synced birthday to dav')
  230. card.fn = bDays[uid][0];
  231. card.bday = bDays[uid][1];
  232. card.rev = bDays[uid][2];
  233. card.validate();
  234. vcard.addressData = vCardToString(card);
  235. davClient.updateCard(cards[uid]);
  236. }
  237. }
  238. })
  239. }
  240. //create in dav
  241. for(const uid in bDays) {
  242. if(!remoteBDays[uid] && bDays[uid][1]) {
  243. engine.log('created birthday in dav')
  244. const card = new VCard({
  245. fn: bDays[uid][0],
  246. bday: new Date(bDays[uid][1]),
  247. rev: bDays[uid].length > 2 ? bDays[uid][2] : new Date(),
  248. note: uid});
  249. card.validate();
  250. davClient.createCard(usedAddressBook, {
  251. data: vCardToString(card),
  252. filename: `${card.uid}.vcf`,
  253. xhr: xhr
  254. });
  255. remoteBDays[uid] = bDays[uid];
  256. }
  257. }
  258. })
  259. }
  260. });
  261. });
  262. function vCardToString(vCard) {
  263. return `BEGIN:VCARD
  264. VERSION:3.0
  265. UID:${vCard.uid}
  266. N:;${vCard.fn};;;
  267. FN:${vCard.fn}
  268. NOTE:${vCard.note && vCard.note[0] ? vCard.note[0] : ""}
  269. REV:${(vCard.rev instanceof Date ? vCard.rev.toISOString().replace(/[-:]|\.000/g, '') : vCard.rev)}
  270. BDAY;VALUE=date:${vCard.bday.toISOString().substring(0, 10).replace(/-/g, '')}
  271. PRODID:-//birthday-script//EN
  272. END:VCARD
  273. `;
  274. }