| 1 | import { moment } from '../../../lib.js'; |
| 2 | import { chat } from '../../../script.js'; |
| 3 | import { timestampToMoment } from '../../utils.js'; |
| 4 | import { MacroRegistry, MacroCategory, MacroValueType } from '../engine/MacroRegistry.js'; |
| 5 | |
| 6 | /** |
| 7 | * Registers time/date related macros and utilities. |
| 8 | */ |
| 9 | export function registerTimeMacros() { |
| 10 | // Time and date macros |
| 11 | MacroRegistry.registerMacro('time', { |
| 12 | category: MacroCategory.TIME, |
| 13 | // Optional single list argument: UTC offset, e.g. {{time::UTC+2}} |
| 14 | unnamedArgs: [ |
| 15 | { |
| 16 | name: 'offset', |
| 17 | optional: true, |
| 18 | defaultValue: 'null', |
| 19 | type: MacroValueType.STRING, |
| 20 | sampleValue: 'UTC+2', |
| 21 | description: 'UTC offset in the format UTC±(offset).', |
| 22 | }, |
| 23 | ], |
| 24 | description: 'Current local time, or UTC offset when called as {{time::UTC±(offset)}}', |
| 25 | returns: 'A time string in the format HH:mm.', |
| 26 | displayOverride: '{{time::[UTC±(offset)]}}', |
| 27 | exampleUsage: ['{{time}}', '{{time::UTC+2}}', '{{time::UTC-7}}'], |
| 28 | handler: ({ unnamedArgs: [offsetSpec] }) => { |
| 29 | if (!offsetSpec) return moment().format('LT'); |
| 30 | |
| 31 | const match = /^UTC([+-]\d+)$/.exec(offsetSpec); |
| 32 | if (!match) return moment().format('LT'); |
| 33 | |
| 34 | const offset = Number.parseInt(match[1], 10); |
| 35 | if (Number.isNaN(offset)) return moment().format('LT'); |
| 36 | |
| 37 | return moment().utc().utcOffset(offset).format('LT'); |
| 38 | }, |
| 39 | }); |
| 40 | |
| 41 | MacroRegistry.registerMacro('date', { |
| 42 | category: MacroCategory.TIME, |
| 43 | description: 'Current local date as a string in the local short format.', |
| 44 | returns: 'Current local date in local short format.', |
| 45 | handler: () => moment().format('LL'), |
| 46 | }); |
| 47 | |
| 48 | MacroRegistry.registerMacro('weekday', { |
| 49 | category: MacroCategory.TIME, |
| 50 | description: 'Current weekday name.', |
| 51 | returns: 'Current weekday name.', |
| 52 | handler: () => moment().format('dddd'), |
| 53 | }); |
| 54 | |
| 55 | MacroRegistry.registerMacro('isotime', { |
| 56 | category: MacroCategory.TIME, |
| 57 | description: 'Current time in HH:mm format.', |
| 58 | returns: 'Current time in HH:mm format.', |
| 59 | handler: () => moment().format('HH:mm'), |
| 60 | }); |
| 61 | |
| 62 | MacroRegistry.registerMacro('isodate', { |
| 63 | category: MacroCategory.TIME, |
| 64 | description: 'Current date in YYYY-MM-DD format.', |
| 65 | returns: 'Current date in YYYY-MM-DD format.', |
| 66 | handler: () => moment().format('YYYY-MM-DD'), |
| 67 | }); |
| 68 | |
| 69 | MacroRegistry.registerMacro('datetimeformat', { |
| 70 | category: MacroCategory.TIME, |
| 71 | unnamedArgs: [ |
| 72 | { |
| 73 | name: 'format', |
| 74 | sampleValue: 'YYYY-MM-DD HH:mm:ss', |
| 75 | description: 'Moment.js format string.', |
| 76 | type: 'string', |
| 77 | }, |
| 78 | ], |
| 79 | description: 'Formats the current date/time using the given moment.js format string.', |
| 80 | returns: 'Formatted date/time string.', |
| 81 | exampleUsage: ['{{datetimeformat::YYYY-MM-DD HH:mm:ss}}', '{{datetimeformat::LLLL}}'], |
| 82 | handler: ({ unnamedArgs: [format] }) => moment().format(format), |
| 83 | }); |
| 84 | |
| 85 | MacroRegistry.registerMacro('idleDuration', { |
| 86 | aliases: [{ alias: 'idle_duration', visible: false }], |
| 87 | category: MacroCategory.TIME, |
| 88 | description: 'Human-readable duration since the last user message.', |
| 89 | returns: 'Human-readable duration since the last user message.', |
| 90 | handler: () => getTimeSinceLastMessage(), |
| 91 | }); |
| 92 | |
| 93 | // Time difference between two values |
| 94 | MacroRegistry.registerMacro('timeDiff', { |
| 95 | category: MacroCategory.TIME, |
| 96 | unnamedArgs: [ |
| 97 | { |
| 98 | name: 'left', |
| 99 | sampleValue: '2023-01-01 12:00:00', |
| 100 | description: 'Left time value.', |
| 101 | type: 'string', |
| 102 | }, |
| 103 | { |
| 104 | name: 'right', |
| 105 | sampleValue: '2023-01-01 15:00:00', |
| 106 | description: 'Right time value.', |
| 107 | type: 'string', |
| 108 | }, |
| 109 | ], |
| 110 | description: 'Human-readable difference between two times. Order of times does not matter, it will return the absolute difference.', |
| 111 | returns: 'Human-readable difference between two times.', |
| 112 | displayOverride: '{{timeDiff::left::right}}', // Shorten this, otherwise it's too long. Full dates don't really help for understanding the macro. |
| 113 | exampleUsage: ['{{ timeDiff :: 2023-01-01 12:00:00 :: 2023-01-01 15:00:00 }}'], |
| 114 | handler: ({ unnamedArgs: [left, right] }) => { |
| 115 | const diff = moment.duration(moment(left).diff(moment(right))); |
| 116 | return diff.humanize(true); |
| 117 | }, |
| 118 | }); |
| 119 | } |
| 120 | |
| 121 | function getTimeSinceLastMessage() { |
| 122 | const now = moment(); |
| 123 | |
| 124 | if (Array.isArray(chat) && chat.length > 0) { |
| 125 | let lastMessage; |
| 126 | let takeNext = false; |
| 127 | |
| 128 | for (let i = chat.length - 1; i >= 0; i--) { |
| 129 | const message = chat[i]; |
| 130 | |
| 131 | if (message.is_system) { |
| 132 | continue; |
| 133 | } |
| 134 | |
| 135 | if (message.is_user && takeNext) { |
| 136 | lastMessage = message; |
| 137 | break; |
| 138 | } |
| 139 | |
| 140 | takeNext = true; |
| 141 | } |
| 142 | |
| 143 | if (lastMessage?.send_date) { |
| 144 | const lastMessageDate = timestampToMoment(lastMessage.send_date); |
| 145 | const duration = moment.duration(now.diff(lastMessageDate)); |
| 146 | return duration.humanize(); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | return 'just now'; |
| 151 | } |