From b67efb12e3ea634f4ce0f81c35d3bd5cda721302 Mon Sep 17 00:00:00 2001 From: function2 Date: Mon, 3 May 2021 15:09:27 +0800 Subject: [PATCH] use luxon library for date & time, augment the module with workaround for microsoft/TypeScript#18877 --- package.json | 2 ++ src/aug.ts | 24 +++++++++++++----------- src/entity/Group.ts | 2 +- src/index.ts | 30 ++++++++++++++++++------------ 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 92da55f..c411f6f 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "description": "一个在北京时间(UTC+8)整点进行报时的 bot。A bot reports time on the hour.", "homepage": "https://github.com/function2-llx/zhengdianbaoshi_bot", "dependencies": { + "luxon": "1.26.0", "mathjs": "9.3.2", "node-telegram-bot-api": "0.53.0", "reflect-metadata": "^0.1.13", @@ -11,6 +12,7 @@ "winston": "3.3.3" }, "devDependencies": { + "@types/luxon": "^1.26.5", "@types/mathjs": "6.0.11", "@types/node": "^15.0.1", "@types/node-telegram-bot-api": "0.51.1", diff --git a/src/aug.ts b/src/aug.ts index 5839259..71e09ee 100644 --- a/src/aug.ts +++ b/src/aug.ts @@ -1,27 +1,29 @@ import * as TelegramBot from 'node-telegram-bot-api'; +// workaround for https://github.com/microsoft/TypeScript/issues/18877 +import { DateTime } from 'luxon/src/datetime' -declare global { - interface Date { +declare module 'luxon/src/datetime' { + interface DateTime { /** * 转为日期+小时的字符串,按照当前 locale 格式 */ toLocaleDateHoursString(): string; - /** - * 获取到下一小时的毫秒数 - * @param minutes 延迟分钟数 - * @param seconds 延迟秒数 - */ + /** + * 获取到下一小时的毫秒数 + * @param minutes 延迟分钟数 + * @param seconds 延迟秒数 + */ getNextHourTimeout(minutes?: number, seconds?: number): number; } } -Date.prototype.toLocaleDateHoursString = function(this: Date): string { - return String([this.toLocaleDateString(), this.getHours()]); +DateTime.prototype.toLocaleDateHoursString = function (this: DateTime): string { + return String([this.toLocaleString(DateTime.DATE_SHORT), this.hour]); } -Date.prototype.getNextHourTimeout = function(this: Date, minutes = 0, seconds = 0): number { - return ((59 - this.getMinutes() + minutes) * 60 + 59 - this.getSeconds() + seconds) * 1000 + 1000 - this.getMilliseconds(); +DateTime.prototype.getNextHourTimeout = function(this: DateTime, minutes = 0, seconds = 0): number { + return ((59 - this.minute + minutes) * 60 + 59 - this.second + seconds) * 1000 + 1000 - this.millisecond; } // It's insane ts does not allow default interface default implementation diff --git a/src/entity/Group.ts b/src/entity/Group.ts index 80ebe93..042096c 100644 --- a/src/entity/Group.ts +++ b/src/entity/Group.ts @@ -43,6 +43,6 @@ export class Group extends BaseEntity { */ static async digest(take: number = -1) { const groups = await Group.find({take}); - return '当前所在群聊:\n' + groups.map(group => `${group.id} ${group.title} ${group.on}\n`); + return '当前所在群聊:\n' + groups.map(group => `${group.id} ${group.title} ${group.on}`).join('\n') + '\n'; } } diff --git a/src/index.ts b/src/index.ts index b220bbb..5318a51 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import * as TelegramBot from 'node-telegram-bot-api' import 'reflect-metadata' import * as typeorm from 'typeorm' import * as math from 'mathjs' +import { DateTime } from 'luxon/src/datetime' import { Group } from './entity/Group'; import { chatIsGroup } from './aug' @@ -31,8 +32,8 @@ const csm = [ const logger = winston.createLogger({ level: 'info', format: winston.format.combine( - winston.format.timestamp(), - winston.format.printf(({level, message, timestamp}) => `${timestamp} ${level.toUpperCase()}: ${message}`), + winston.format.timestamp({ format: () => DateTime.now().toISO() }), + winston.format.printf(({level, message, timestamp}) => `${timestamp} ${level}: ${message}`), ), transports: [ new winston.transports.Console(), @@ -73,20 +74,23 @@ typeorm.createConnection().then(async db => { let lastReport: Record = {}; async function setupReport() { for (;;) { - const timeout = new Date().getNextHourTimeout(1); + const timeout = DateTime.now().getNextHourTimeout(1); logger.info(`setup timeout:${timeout}ms`); await new Promise(resolve => setTimeout(resolve, timeout)); - const d = new Date(); - const hours12 = d.getHours() % 12; + const d = DateTime.now(); + const hours12 = d.hour % 12; const cur = d.toLocaleDateHoursString(); - const groups = await Group.find(); + const groups = await Group.find({ + select: ['id', 'title'], + where: { on: true }, + }); // 等待所有组都发完 await Promise.all(groups.map(async group => { if (lastReport[group.id] === cur) { - logger.info(`${d.toLocaleString()}:${group.id}群友已报时,跳过`); + logger.info(`${d.toLocaleString()}:${group.name()}群友已报时,跳过`); } else { await bot.sendSticker(group.id, stickers[hours12]); - logger.info(`${d.toLocaleString()}:成功向${group.id}报时`); + logger.info(`${d.toLocaleString()}:成功向${group.name()}报时`); } })); lastReport = {}; @@ -124,12 +128,14 @@ typeorm.createConnection().then(async db => { break; } case 'baoshi': { - await bot.sendSticker(chat.id, stickers[new Date().getHours() % 12]); + const d = DateTime.now(); + await bot.sendSticker(chat.id, stickers[d.hour % 12]); + lastReport[chat.id] = d.toLocaleDateHoursString(); break; } case 'time': { // bot 所处服务器 locale 设为 zh_CN.utf8 - await bot.sendMessage(chat.id, new Date().toLocaleString()); + await bot.sendMessage(chat.id, DateTime.now().toLocaleString(DateTime.DATETIME_HUGE_WITH_SECONDS)); break; } case 'off': @@ -169,8 +175,8 @@ typeorm.createConnection().then(async db => { break; } case 'group': { - const d = new Date(); - if (stickersInv[sticker.file_id] === d.getHours()) { + const d = DateTime.now(); + if (stickersInv[sticker.file_id] === d.hour) { // 记录群友报时 lastReport[chat.id] = d.toLocaleDateHoursString(); }