koishi-plugin-anime-schedule 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/config.js CHANGED
@@ -1,6 +1,10 @@
1
- import { Schema } from 'koishi';
2
- import { COVER_CONCURRENCY, DEFAULT_FONT_FAMILY, DEFAULT_JPEG_QUALITY } from './constants';
3
- import { resolveTimeZone } from './utils/time';
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Config = void 0;
4
+ exports.normalizeConfig = normalizeConfig;
5
+ const koishi_1 = require("koishi");
6
+ const constants_1 = require("./constants");
7
+ const time_1 = require("./utils/time");
4
8
  const TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
5
9
  const WEEKDAYS = [
6
10
  { value: 0, label: '周日' },
@@ -11,150 +15,152 @@ const WEEKDAYS = [
11
15
  { value: 5, label: '周五' },
12
16
  { value: 6, label: '周六' },
13
17
  ];
14
- export const Config = Schema.intersect([
15
- Schema.object({
16
- timeZone: Schema.union([
17
- Schema.const('Asia/Shanghai').description('Asia/Shanghai (UTC+8)'),
18
- Schema.const('Asia/Tokyo').description('Asia/Tokyo (UTC+9)'),
19
- Schema.const('Asia/Hong_Kong').description('Asia/Hong_Kong (UTC+8)'),
20
- Schema.const('Asia/Taipei').description('Asia/Taipei (UTC+8)'),
21
- Schema.const('UTC').description('UTC'),
18
+ exports.Config = koishi_1.Schema.intersect([
19
+ koishi_1.Schema.object({
20
+ timeZone: koishi_1.Schema.union([
21
+ koishi_1.Schema.const('Asia/Shanghai').description('Asia/Shanghai (UTC+8)'),
22
+ koishi_1.Schema.const('Asia/Tokyo').description('Asia/Tokyo (UTC+9)'),
23
+ koishi_1.Schema.const('Asia/Hong_Kong').description('Asia/Hong_Kong (UTC+8)'),
24
+ koishi_1.Schema.const('Asia/Taipei').description('Asia/Taipei (UTC+8)'),
25
+ koishi_1.Schema.const('UTC').description('UTC'),
22
26
  ])
23
27
  .description('时刻与日期按此时区换算')
24
28
  .default('Asia/Shanghai'),
25
- locale: Schema.union([
26
- Schema.const('zh-CN').description('简体中文'),
27
- Schema.const('ja-JP').description('日本語'),
28
- Schema.const('en-US').description('English'),
29
+ locale: koishi_1.Schema.union([
30
+ koishi_1.Schema.const('zh-CN').description('简体中文'),
31
+ koishi_1.Schema.const('ja-JP').description('日本語'),
32
+ koishi_1.Schema.const('en-US').description('English'),
29
33
  ])
30
34
  .description('界面语言')
31
35
  .default('zh-CN'),
32
36
  }),
33
- Schema.object({
34
- push: Schema.object({
35
- dailyEnabled: Schema.boolean().description('每日更新表').default(true),
36
- dailyTime: Schema.string().pattern(TIME_PATTERN).description('推送时刻').default('08:00'),
37
- dailyOffsetDays: Schema.number()
37
+ koishi_1.Schema.object({
38
+ push: koishi_1.Schema.object({
39
+ dailyEnabled: koishi_1.Schema.boolean().description('每日更新表').default(true),
40
+ dailyTime: koishi_1.Schema.string().pattern(TIME_PATTERN).description('推送时刻').default('08:00'),
41
+ dailyOffsetDays: koishi_1.Schema.number()
38
42
  .min(-1).max(1).step(1)
39
43
  .description('日期偏移:0 当天,-1 前一天,1 后一天')
40
44
  .default(0),
41
- nightEnabled: Schema.boolean().description('深夜档(当晚 21:00 至次日 06:00)').default(true),
42
- nightTime: Schema.string().pattern(TIME_PATTERN).description('推送时刻').default('21:00'),
43
- weeklyEnabled: Schema.boolean().description('本周更新表').default(true),
44
- weeklyWeekday: Schema.union(WEEKDAYS.map((item) => Schema.const(item.value).description(item.label)))
45
+ nightEnabled: koishi_1.Schema.boolean().description('深夜档(当晚 21:00 至次日 06:00)').default(true),
46
+ nightTime: koishi_1.Schema.string().pattern(TIME_PATTERN).description('推送时刻').default('21:00'),
47
+ weeklyEnabled: koishi_1.Schema.boolean().description('本周更新表').default(true),
48
+ weeklyWeekday: koishi_1.Schema.union(WEEKDAYS.map((item) => koishi_1.Schema.const(item.value).description(item.label)))
45
49
  .description('周表的推送星期')
46
50
  .default(1),
47
- weeklyTime: Schema.string().pattern(TIME_PATTERN).description('推送时刻').default('08:00'),
48
- seasonEnabled: Schema.boolean().description('本季新番表').default(true),
49
- seasonTime: Schema.string().pattern(TIME_PATTERN).description('季度首日的推送时刻').default('08:00'),
50
- updateReminderEnabled: Schema.boolean().description('订阅作品的开播提醒').default(true),
51
- updateLookaheadMinutes: Schema.number()
51
+ weeklyTime: koishi_1.Schema.string().pattern(TIME_PATTERN).description('推送时刻').default('08:00'),
52
+ seasonEnabled: koishi_1.Schema.boolean().description('本季新番表').default(true),
53
+ seasonTime: koishi_1.Schema.string().pattern(TIME_PATTERN).description('季度首日的推送时刻').default('08:00'),
54
+ updateReminderEnabled: koishi_1.Schema.boolean().description('订阅作品的开播提醒').default(true),
55
+ updateLookaheadMinutes: koishi_1.Schema.number()
52
56
  .min(1).step(1)
53
57
  .description('提前多少分钟提醒')
54
58
  .default(30),
55
- updatePollMinutes: Schema.number()
59
+ updatePollMinutes: koishi_1.Schema.number()
56
60
  .min(1).step(1)
57
61
  .description('检查间隔(分钟)')
58
62
  .default(10),
59
63
  }),
60
64
  }).description('推送设置'),
61
- Schema.object({
62
- content: Schema.object({
63
- maxItems: Schema.number()
65
+ koishi_1.Schema.object({
66
+ content: koishi_1.Schema.object({
67
+ maxItems: koishi_1.Schema.number()
64
68
  .min(0).step(10)
65
69
  .description('单次推送最多显示多少部,0 为不限制')
66
70
  .default(0),
67
- longRunningThreshold: Schema.number()
71
+ longRunningThreshold: koishi_1.Schema.number()
68
72
  .min(0).step(1)
69
- .description('剔除长期连载番的话数阈值,0 为不过滤')
73
+ .description('剔除长期连载番的话数阈值,0 为不过滤。'
74
+ + '展示话数达到该值的作品整条不显示——海螺小姐、宝可梦 地平线这类周更多年的作品'
75
+ + '话数会一直涨,而一季 12~24 话的当季番永远达不到,所以填 64 这类值即可')
70
76
  .default(0),
71
77
  }),
72
78
  }).description('推送内容'),
73
- Schema.object({
74
- output: Schema.object({
75
- format: Schema.union([
76
- Schema.const('image').description('风格化图片'),
77
- Schema.const('text').description('纯文本'),
79
+ koishi_1.Schema.object({
80
+ output: koishi_1.Schema.object({
81
+ format: koishi_1.Schema.union([
82
+ koishi_1.Schema.const('image').description('风格化图片'),
83
+ koishi_1.Schema.const('text').description('纯文本'),
78
84
  ])
79
85
  .description('推送形态;图片渲染失败时自动改用文本')
80
86
  .default('image'),
81
- layout: Schema.union([
82
- Schema.const('table').description('时间表'),
83
- Schema.const('list').description('卡片列表'),
87
+ layout: koishi_1.Schema.union([
88
+ koishi_1.Schema.const('table').description('时间表'),
89
+ koishi_1.Schema.const('list').description('卡片列表'),
84
90
  ])
85
91
  .description('图片排版')
86
92
  .default('table'),
87
- coverLimit: Schema.number()
93
+ coverLimit: koishi_1.Schema.number()
88
94
  .min(0)
89
95
  .description('单张图片最多加载多少张封面,0 为不限制')
90
96
  .default(0),
91
- showCover: Schema.boolean().description('显示封面').default(true),
92
- showScore: Schema.boolean().description('显示评分').default(true),
93
- useForward: Schema.boolean()
97
+ showCover: koishi_1.Schema.boolean().description('显示封面').default(true),
98
+ showScore: koishi_1.Schema.boolean().description('显示评分').default(true),
99
+ useForward: koishi_1.Schema.boolean()
94
100
  .description('纯文本模式使用合并转发,避免刷屏')
95
101
  .default(true),
96
102
  }),
97
103
  }).description('输出设置'),
98
- Schema.object({
99
- localize: Schema.object({
100
- enabled: Schema.boolean()
104
+ koishi_1.Schema.object({
105
+ localize: koishi_1.Schema.object({
106
+ enabled: koishi_1.Schema.boolean()
101
107
  .description('使用中文标题与评分')
102
108
  .default(true),
103
- titleStyle: Schema.union([
104
- Schema.const('localized').description('仅中文'),
105
- Schema.const('both').description('中文(日文原名)'),
106
- Schema.const('original').description('仅日文原名'),
109
+ titleStyle: koishi_1.Schema.union([
110
+ koishi_1.Schema.const('localized').description('仅中文'),
111
+ koishi_1.Schema.const('both').description('中文(日文原名)'),
112
+ koishi_1.Schema.const('original').description('仅日文原名'),
107
113
  ])
108
114
  .description('标题展示方式')
109
115
  .default('localized'),
110
- cacheTtlDays: Schema.number()
116
+ cacheTtlDays: koishi_1.Schema.number()
111
117
  .min(1).step(1)
112
118
  .description('标题、封面与评分的缓存天数')
113
119
  .default(30),
114
- airingTtlHours: Schema.number()
120
+ airingTtlHours: koishi_1.Schema.number()
115
121
  .min(1).step(1)
116
122
  .description('播出状态(是否已播完)的缓存小时数')
117
123
  .default(6),
118
124
  }),
119
125
  }).description('本地化'),
120
- Schema.object({
121
- render: Schema.object({
122
- fontFamily: Schema.string()
126
+ koishi_1.Schema.object({
127
+ render: koishi_1.Schema.object({
128
+ fontFamily: koishi_1.Schema.string()
123
129
  .description('绘制图片时使用的字体族;留空则自动寻找系统中文字体')
124
- .default(DEFAULT_FONT_FAMILY),
125
- coverConcurrency: Schema.number()
130
+ .default(constants_1.DEFAULT_FONT_FAMILY),
131
+ coverConcurrency: koishi_1.Schema.number()
126
132
  .min(1)
127
133
  .description('封面下载并发数')
128
- .default(COVER_CONCURRENCY),
129
- coverCacheDays: Schema.number()
134
+ .default(constants_1.COVER_CONCURRENCY),
135
+ coverCacheDays: koishi_1.Schema.number()
130
136
  .min(0)
131
137
  .description('封面缓存天数,0 为不写磁盘')
132
138
  .default(21),
133
- jpegQuality: Schema.number()
139
+ jpegQuality: koishi_1.Schema.number()
134
140
  .min(1).max(100).step(1)
135
141
  .description('图片编码质量')
136
- .default(DEFAULT_JPEG_QUALITY),
142
+ .default(constants_1.DEFAULT_JPEG_QUALITY),
137
143
  }),
138
144
  }).description('图片'),
139
- Schema.object({
140
- subscription: Schema.object({
141
- maxPerChannel: Schema.number()
145
+ koishi_1.Schema.object({
146
+ subscription: koishi_1.Schema.object({
147
+ maxPerChannel: koishi_1.Schema.number()
142
148
  .min(0).step(1)
143
149
  .description('单个群最多订阅多少部,0 为不限制')
144
150
  .default(0),
145
151
  }),
146
152
  }).description('订阅'),
147
- Schema.object({
148
- advanced: Schema.object({
149
- requestTimeout: Schema.number()
153
+ koishi_1.Schema.object({
154
+ advanced: koishi_1.Schema.object({
155
+ requestTimeout: koishi_1.Schema.number()
150
156
  .min(1)
151
157
  .description('单次请求超时(秒)')
152
158
  .default(20),
153
- retries: Schema.number()
159
+ retries: koishi_1.Schema.number()
154
160
  .min(0)
155
161
  .description('请求失败重试次数')
156
162
  .default(2),
157
- debug: Schema.boolean()
163
+ debug: koishi_1.Schema.boolean()
158
164
  .description('输出调试日志')
159
165
  .default(false),
160
166
  }),
@@ -164,7 +170,7 @@ export const Config = Schema.intersect([
164
170
  * 把配置夹到安全区间。
165
171
  * 只保证「类型正确」与「不小于最小值」,不设人为上限。
166
172
  */
167
- export function normalizeConfig(config) {
173
+ function normalizeConfig(config) {
168
174
  const int = (value, fallback, min = 0) => {
169
175
  if (typeof value !== 'number' || !Number.isFinite(value))
170
176
  return fallback;
@@ -175,7 +181,7 @@ export function normalizeConfig(config) {
175
181
  const format = config.output?.format === 'text' ? 'text' : 'image';
176
182
  return {
177
183
  ...config,
178
- timeZone: resolveTimeZone(config.timeZone, 'Asia/Shanghai'),
184
+ timeZone: (0, time_1.resolveTimeZone)(config.timeZone, 'Asia/Shanghai'),
179
185
  push: {
180
186
  ...config.push,
181
187
  dailyTime: time(config.push?.dailyTime, '08:00'),
@@ -204,10 +210,10 @@ export function normalizeConfig(config) {
204
210
  },
205
211
  render: {
206
212
  ...config.render,
207
- fontFamily: config.render?.fontFamily?.trim() || DEFAULT_FONT_FAMILY,
208
- coverConcurrency: int(config.render?.coverConcurrency, COVER_CONCURRENCY, 1),
213
+ fontFamily: config.render?.fontFamily?.trim() || constants_1.DEFAULT_FONT_FAMILY,
214
+ coverConcurrency: int(config.render?.coverConcurrency, constants_1.COVER_CONCURRENCY, 1),
209
215
  coverCacheDays: int(config.render?.coverCacheDays, 21),
210
- jpegQuality: Math.min(100, int(config.render?.jpegQuality, DEFAULT_JPEG_QUALITY, 1)),
216
+ jpegQuality: Math.min(100, int(config.render?.jpegQuality, constants_1.DEFAULT_JPEG_QUALITY, 1)),
211
217
  },
212
218
  subscription: {
213
219
  ...config.subscription,
package/lib/constants.js CHANGED
@@ -1,23 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LOG_MESSAGES = exports.RESPONSES = exports.MESSAGES = exports.IMAGE_THEME = exports.IMAGE_LAYOUT = exports.DEFAULT_FONT_FAMILY = exports.DEFAULT_JPEG_QUALITY = exports.BOLD_FONT_CANDIDATES = exports.FONT_CANDIDATES = exports.COVER_CACHE_MAX_FILES = exports.COVER_CACHE_TTL_MS = exports.COVER_CACHE_DIR = exports.COVER_MAX_BYTES = exports.COVER_TIMEOUT_MS = exports.COVER_CONCURRENCY = exports.LOCALIZE_CONCURRENCY = exports.EPISODE_QUERY_LIMIT = exports.LOCALIZE_CACHE_MAX_ENTRIES = exports.LOCALIZE_CACHE_VERSION = exports.LOCALIZE_CACHE_FILE = exports.LATE_NIGHT_END_HOUR = exports.IMAGE_LIST_TWO_COLUMN_AT = exports.IMAGE_MAX_CARDS = exports.DIGEST_LABELS = exports.PLUGIN_HOMEPAGE = exports.PLUGIN_VERSION = exports.DATA_DIR = exports.BANGUMI_DATA_FILE = exports.BANGUMI_DATA_FALLBACK_URLS = exports.ANILIST_MIN_INTERVAL_MS = exports.ANILIST_BATCH_SIZE = exports.ANILIST_API_URL = exports.BANGUMI_API_HOST = exports.PLUGIN_NAME = void 0;
4
+ exports.buildUserAgent = buildUserAgent;
5
+ exports.getMessages = getMessages;
1
6
  /** 插件名,同时作为日志前缀与 User-Agent 主体 */
2
- export const PLUGIN_NAME = 'anime-schedule';
7
+ exports.PLUGIN_NAME = 'anime-schedule';
3
8
  /** Bangumi API 主机 */
4
- export const BANGUMI_API_HOST = 'https://api.bgm.tv';
9
+ exports.BANGUMI_API_HOST = 'https://api.bgm.tv';
5
10
  /**
6
11
  * AniList GraphQL 接口。
7
12
  *
8
13
  * 用于查询「这部番是否还在播」:它直接给出 `status`(FINISHED / RELEASING)与 `endDate`,
9
14
  * 而 bangumi-data 自带 `aniList` 的条目 ID,可以按 ID 精确查询、避开标题匹配歧义。
10
15
  */
11
- export const ANILIST_API_URL = 'https://graphql.anilist.co';
16
+ exports.ANILIST_API_URL = 'https://graphql.anilist.co';
12
17
  /** 单次 GraphQL 请求最多查多少个条目 */
13
- export const ANILIST_BATCH_SIZE = 50;
18
+ exports.ANILIST_BATCH_SIZE = 50;
14
19
  /**
15
20
  * 两次 AniList 请求之间的最小间隔(毫秒)。
16
21
  *
17
22
  * 官方限制 90 次/分钟(实测响应头为 30),另有突发限流。留出 1.2 秒间隔后
18
23
  * 相当于最多 50 次/分钟,既不会触发突发限流,也远低于上限。
19
24
  */
20
- export const ANILIST_MIN_INTERVAL_MS = 1200;
25
+ exports.ANILIST_MIN_INTERVAL_MS = 1200;
21
26
  /**
22
27
  * bangumi-data 索引。
23
28
  *
@@ -25,26 +30,26 @@ export const ANILIST_MIN_INTERVAL_MS = 1200;
25
30
  * 随插件一起安装、无需联网;下面这些地址只在依赖包缺失时才作为回退。
26
31
  * 仓库: https://github.com/bangumi-data/bangumi-data
27
32
  */
28
- export const BANGUMI_DATA_FALLBACK_URLS = [
33
+ exports.BANGUMI_DATA_FALLBACK_URLS = [
29
34
  'https://cdn.jsdelivr.net/npm/bangumi-data@latest/dist/data.json',
30
35
  'https://unpkg.com/bangumi-data@latest/dist/data.json',
31
36
  'https://raw.githubusercontent.com/bangumi-data/bangumi-data/master/dist/data.json',
32
37
  ];
33
- export const BANGUMI_DATA_FILE = 'bangumi-data.json';
38
+ exports.BANGUMI_DATA_FILE = 'bangumi-data.json';
34
39
  /** bangumi-data 回退缓存的目录(相对于 ctx.baseDir),与本地化缓存放在一起 */
35
- export const DATA_DIR = ['data', 'anime-schedule'];
40
+ exports.DATA_DIR = ['data', 'anime-schedule'];
36
41
  /** 插件版本与主页,请求 Bangumi 时用于自报身份 */
37
- export const PLUGIN_VERSION = '1.0.0';
38
- export const PLUGIN_HOMEPAGE = 'https://github.com/YuzuharaYuka/koishi-plugin-anime-schedule';
42
+ exports.PLUGIN_VERSION = '1.0.0';
43
+ exports.PLUGIN_HOMEPAGE = 'https://github.com/YuzuharaYuka/koishi-plugin-anime-schedule';
39
44
  /**
40
45
  * 请求 Bangumi 时使用的 User-Agent。
41
46
  * Bangumi 要求客户端自报身份,形如 `AppName/version (+主页)`。
42
47
  */
43
- export function buildUserAgent() {
44
- return `${PLUGIN_NAME}/${PLUGIN_VERSION} (+${PLUGIN_HOMEPAGE})`;
48
+ function buildUserAgent() {
49
+ return `${exports.PLUGIN_NAME}/${exports.PLUGIN_VERSION} (+${exports.PLUGIN_HOMEPAGE})`;
45
50
  }
46
51
  /** 各类推送的展示名,用于日志、合并转发标题与开关回复 */
47
- export const DIGEST_LABELS = {
52
+ exports.DIGEST_LABELS = {
48
53
  daily: '每日更新表',
49
54
  night: '深夜档',
50
55
  weekly: '本周更新表',
@@ -52,9 +57,9 @@ export const DIGEST_LABELS = {
52
57
  sub: '番剧订阅',
53
58
  };
54
59
  /** 卡片列表排版绘制的卡片数上限,避免条目异常多时图片长度失控 */
55
- export const IMAGE_MAX_CARDS = 200;
60
+ exports.IMAGE_MAX_CARDS = 200;
56
61
  /** 列表排版超过这个数量就折成两栏,避免长图过长 */
57
- export const IMAGE_LIST_TWO_COLUMN_AT = 26;
62
+ exports.IMAGE_LIST_TWO_COLUMN_AT = 26;
58
63
  /**
59
64
  * 「深夜番」的判定:本地时间 0 点到 6 点算作前一日的深夜档。
60
65
  *
@@ -62,9 +67,9 @@ export const IMAGE_LIST_TWO_COLUMN_AT = 26;
62
67
  * 改一处时另一处也要跟着改。这里的用途是统计与文案(`finish()` 里的「其中 N 部为深夜档」、
63
68
  * 时间轴的 24 小时制续写),不是窗口本身。
64
69
  */
65
- export const LATE_NIGHT_END_HOUR = 6;
70
+ exports.LATE_NIGHT_END_HOUR = 6;
66
71
  /** 本地化缓存的落盘文件名 */
67
- export const LOCALIZE_CACHE_FILE = 'localize-cache.json';
72
+ exports.LOCALIZE_CACHE_FILE = 'localize-cache.json';
68
73
  /**
69
74
  * 本地化缓存的结构版本。
70
75
  *
@@ -75,28 +80,28 @@ export const LOCALIZE_CACHE_FILE = 'localize-cache.json';
75
80
  * v6:新增 `hasEpisodeTimes`。逐集档期不再是每条必取,需要靠它区分
76
81
  * 「没取过逐集档期」与「取过但没有集数日期」。
77
82
  */
78
- export const LOCALIZE_CACHE_VERSION = 6;
83
+ exports.LOCALIZE_CACHE_VERSION = 6;
79
84
  /** 本地化缓存最大条目数,超出后按写入时间淘汰最旧的一半 */
80
- export const LOCALIZE_CACHE_MAX_ENTRIES = 5000;
85
+ exports.LOCALIZE_CACHE_MAX_ENTRIES = 5000;
81
86
  /** 取逐集档期时的单页集数上限;超过这个数的长期番不靠集数判断完结 */
82
- export const EPISODE_QUERY_LIMIT = 100;
87
+ exports.EPISODE_QUERY_LIMIT = 100;
83
88
  /**
84
89
  * 并发检索 Bangumi 的条数上限。
85
90
  * Bangumi 没有公开的严格限流,但一次本季表可能有上百部作品,
86
91
  * 4 路并发已能把原本串行的等待时间压到 1/4,同时不至于触发风控。
87
92
  */
88
- export const LOCALIZE_CONCURRENCY = 4;
93
+ exports.LOCALIZE_CONCURRENCY = 4;
89
94
  /** 并发拉取封面时的并发上限 */
90
- export const COVER_CONCURRENCY = 8;
95
+ exports.COVER_CONCURRENCY = 8;
91
96
  /** 封面下载超时与体积上限,避免个别大图拖垮整次渲染 */
92
- export const COVER_TIMEOUT_MS = 8000;
93
- export const COVER_MAX_BYTES = 4 * 1024 * 1024;
97
+ exports.COVER_TIMEOUT_MS = 8000;
98
+ exports.COVER_MAX_BYTES = 4 * 1024 * 1024;
94
99
  /** 封面磁盘缓存目录(相对于 ctx.baseDir)与有效期 */
95
- export const COVER_CACHE_DIR = ['data', 'anime-schedule', 'covers'];
96
- export const COVER_CACHE_TTL_MS = 21 * 24 * 60 * 60 * 1000;
97
- export const COVER_CACHE_MAX_FILES = 1200;
100
+ exports.COVER_CACHE_DIR = ['data', 'anime-schedule', 'covers'];
101
+ exports.COVER_CACHE_TTL_MS = 21 * 24 * 60 * 60 * 1000;
102
+ exports.COVER_CACHE_MAX_FILES = 1200;
98
103
  /** 图片渲染用的字体候选,按平台常见路径排列,找不到时回退到运行环境默认字体 */
99
- export const FONT_CANDIDATES = [
104
+ exports.FONT_CANDIDATES = [
100
105
  'C:/Windows/Fonts/msyh.ttc',
101
106
  'C:/Windows/Fonts/msyh.ttf',
102
107
  'C:/Windows/Fonts/simhei.ttf',
@@ -112,7 +117,7 @@ export const FONT_CANDIDATES = [
112
117
  '/usr/share/fonts/truetype/arphic/uming.ttc',
113
118
  ];
114
119
  /** 加粗字体候选,找不到时由绘制层用描边模拟粗体 */
115
- export const BOLD_FONT_CANDIDATES = [
120
+ exports.BOLD_FONT_CANDIDATES = [
116
121
  'C:/Windows/Fonts/msyhbd.ttc',
117
122
  'C:/Windows/Fonts/Dengb.ttf',
118
123
  'C:/Windows/Fonts/simhei.ttf',
@@ -127,9 +132,9 @@ export const BOLD_FONT_CANDIDATES = [
127
132
  * 是**无损**编码,文字边缘最锐利;体积比 92 大约多三成,但一张周表也就一兆出头,
128
133
  * 不值得为省这点流量牺牲清晰度。想压体积可以调低。
129
134
  */
130
- export const DEFAULT_JPEG_QUALITY = 100;
135
+ exports.DEFAULT_JPEG_QUALITY = 100;
131
136
  /** 注册到 canvas 的字体族名,与 config.render.fontFamily 的默认值保持一致 */
132
- export const DEFAULT_FONT_FAMILY = 'Anime Schedule Sans';
137
+ exports.DEFAULT_FONT_FAMILY = 'Anime Schedule Sans';
133
138
  /**
134
139
  * 图片版式常量。
135
140
  *
@@ -140,7 +145,7 @@ export const DEFAULT_FONT_FAMILY = 'Anime Schedule Sans';
140
145
  * 与文字长短无关,因此整张图的每一行都对齐在同样的基线上。
141
146
  * - **封面**:统一 `媒体高 × 1.4` 的竖版比例,等比完整显示,不裁切。
142
147
  */
143
- export const IMAGE_LAYOUT = {
148
+ exports.IMAGE_LAYOUT = {
144
149
  /** 统一外边距 */
145
150
  padding: 24,
146
151
  /** 页眉 / 页脚固定高度 */
@@ -222,7 +227,7 @@ export const IMAGE_LAYOUT = {
222
227
  seasonCoverRatio: 0.72,
223
228
  };
224
229
  /** 时间表配色,全插件共用一套色板 */
225
- export const IMAGE_THEME = {
230
+ exports.IMAGE_THEME = {
226
231
  background: '#f4f5f8',
227
232
  surface: '#ffffff',
228
233
  cardBorder: '#e6e8ef',
@@ -325,16 +330,16 @@ const EN_US = {
325
330
  subscribedBadge: 'Subscribed',
326
331
  moreItems: (count) => `+${count} more`,
327
332
  };
328
- export const MESSAGES = {
333
+ exports.MESSAGES = {
329
334
  'zh-CN': ZH_CN,
330
335
  'ja-JP': JA_JP,
331
336
  'en-US': EN_US,
332
337
  };
333
- export function getMessages(locale) {
334
- return MESSAGES[locale] ?? ZH_CN;
338
+ function getMessages(locale) {
339
+ return exports.MESSAGES[locale] ?? ZH_CN;
335
340
  }
336
341
  /** 交互回复:完整句,结尾带句号 */
337
- export const RESPONSES = {
342
+ exports.RESPONSES = {
338
343
  needGuild: '请在群聊中使用此指令。',
339
344
  invalidKind: (value) => `无效的推送类型: ${value},可选值为 daily、night、weekly、season。`,
340
345
  invalidDigest: (value) => `无效的推送类型: ${value},可选值为 daily、night、weekly、season。`,
@@ -350,6 +355,6 @@ export const RESPONSES = {
350
355
  notFound: '未找到该作品,它可能不在 bangumi-data 索引里。',
351
356
  };
352
357
  /** 日志里出现的固定短语 */
353
- export const LOG_MESSAGES = {
358
+ exports.LOG_MESSAGES = {
354
359
  cleanupFailed: '清理过期推送记录失败:',
355
360
  };
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /**
2
3
  * 按标题把条目匹配到 AniList。
3
4
  *
@@ -12,10 +13,14 @@
12
13
  * 与日本电视档期本来就可能差几小时(配信早于电视),因此只用来提示「档期需订正」,
13
14
  * 不作为匹配是否成立的条件。
14
15
  */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.MAX_HOUR_GAP = exports.MAX_DAY_GAP = void 0;
18
+ exports.normalizeTitle = normalizeTitle;
19
+ exports.matchByTitle = matchByTitle;
15
20
  /** 首播日期允许的偏差(天) */
16
- export const MAX_DAY_GAP = 30;
21
+ exports.MAX_DAY_GAP = 30;
17
22
  /** 播出时刻允许的偏差(小时);仅用于判断档期是否需订正 */
18
- export const MAX_HOUR_GAP = 6;
23
+ exports.MAX_HOUR_GAP = 6;
19
24
  const HOUR = 3600000;
20
25
  const DAY = 86400000;
21
26
  /**
@@ -24,7 +29,7 @@ const DAY = 86400000;
24
29
  * 目的是让「転生貴族、鑑定スキルで成り上がる(第3期)」与
25
30
  * 「転生貴族、鑑定スキルで成り上がる 第3期」这类写法差异不影响匹配。
26
31
  */
27
- export function normalizeTitle(value) {
32
+ function normalizeTitle(value) {
28
33
  return String(value ?? '')
29
34
  .toLowerCase()
30
35
  .replace(/[0-9a-zA-Z]/g, (char) => String.fromCharCode(char.charCodeAt(0) - 0xfee0))
@@ -45,7 +50,7 @@ function startMsOf(candidate) {
45
50
  * 返回 null 表示没有候选满足硬条件——此时应当**放弃匹配**,退回周期规则的推算,
46
51
  * 而不是猜一个「看起来像」的。
47
52
  */
48
- export function matchByTitle(target, candidates) {
53
+ function matchByTitle(target, candidates) {
49
54
  const wanted = normalizeTitle(target.title);
50
55
  if (!wanted)
51
56
  return null;
@@ -63,7 +68,7 @@ export function matchByTitle(target, candidates) {
63
68
  continue;
64
69
  const startMs = startMsOf(candidate);
65
70
  const dayGap = startMs !== null && beginMs !== null ? Math.abs(startMs - beginMs) / DAY : null;
66
- if (dayGap !== null && dayGap > MAX_DAY_GAP)
71
+ if (dayGap !== null && dayGap > exports.MAX_DAY_GAP)
67
72
  continue;
68
73
  const nodes = (candidate.airingSchedule?.nodes ?? []).filter((node) => typeof node.airingAt === 'number');
69
74
  const first = nodes.length
@@ -72,7 +77,7 @@ export function matchByTitle(target, candidates) {
72
77
  const hourGap = first && Number.isFinite(topMs)
73
78
  ? Math.abs(first.airingAt * 1000 - topMs) / HOUR
74
79
  : null;
75
- const timeAgrees = hourGap === null || hourGap <= MAX_HOUR_GAP;
80
+ const timeAgrees = hourGap === null || hourGap <= exports.MAX_HOUR_GAP;
76
81
  const current = { candidate, dayGap, hourGap, timeAgrees };
77
82
  // 优先取「时刻也对得上」的候选;同类则取首播日期更接近的
78
83
  const better = !best
@@ -1,3 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BangumiDataLoader = void 0;
1
4
  /**
2
5
  * bangumi-data 的取数层。
3
6
  *
@@ -6,26 +9,26 @@
6
9
  *
7
10
  * 这一层只负责「把 item 列表拿回来」,不解析、不建索引。
8
11
  */
9
- import { promises as fs } from 'fs';
10
- import { dirname, resolve } from 'path';
11
- import { BANGUMI_DATA_FALLBACK_URLS, BANGUMI_DATA_FILE, DATA_DIR } from '../constants';
12
- import { errorMessage, unwrapText } from '../utils';
13
- import { HttpClient } from '../services/network';
12
+ const fs_1 = require("fs");
13
+ const path_1 = require("path");
14
+ const constants_1 = require("../constants");
15
+ const utils_1 = require("../utils");
16
+ const network_1 = require("../services/network");
14
17
  /** 只在缓存里保留用得到的字段,避免把整个包写一遍 */
15
18
  const CACHED_FIELDS = [
16
19
  'title', 'titleTranslate', 'type', 'officialSite', 'begin', 'broadcast', 'end', 'sites',
17
20
  ];
18
- export class BangumiDataLoader {
21
+ class BangumiDataLoader {
19
22
  ctx;
20
23
  logger;
21
24
  http;
22
25
  constructor(ctx, logger) {
23
26
  this.ctx = ctx;
24
27
  this.logger = logger;
25
- this.http = new HttpClient(ctx, logger);
28
+ this.http = new network_1.HttpClient(ctx, logger);
26
29
  }
27
30
  get cachePath() {
28
- return resolve(this.ctx.baseDir, ...DATA_DIR, BANGUMI_DATA_FILE);
31
+ return (0, path_1.resolve)(this.ctx.baseDir, ...constants_1.DATA_DIR, constants_1.BANGUMI_DATA_FILE);
29
32
  }
30
33
  /** 依次尝试:npm 依赖包 → 磁盘缓存 → CDN 镜像 */
31
34
  async load() {
@@ -51,20 +54,20 @@ export class BangumiDataLoader {
51
54
  return items;
52
55
  }
53
56
  catch (error) {
54
- this.logger.debug('[index] 本地依赖包不可用: %s', errorMessage(error));
57
+ this.logger.debug('[index] 本地依赖包不可用: %s', (0, utils_1.errorMessage)(error));
55
58
  }
56
59
  return null;
57
60
  }
58
61
  async loadFromCache() {
59
62
  try {
60
- const text = await fs.readFile(this.cachePath, 'utf8');
63
+ const text = await fs_1.promises.readFile(this.cachePath, 'utf8');
61
64
  const parsed = JSON.parse(text);
62
65
  if (Array.isArray(parsed.items) && parsed.items.length)
63
66
  return parsed.items;
64
67
  }
65
68
  catch (error) {
66
69
  if (error.code !== 'ENOENT') {
67
- this.logger.debug('[index] 读取缓存失败: %s', errorMessage(error));
70
+ this.logger.debug('[index] 读取缓存失败: %s', (0, utils_1.errorMessage)(error));
68
71
  }
69
72
  }
70
73
  return null;
@@ -72,7 +75,7 @@ export class BangumiDataLoader {
72
75
  async writeCache(items) {
73
76
  const temp = `${this.cachePath}.tmp`;
74
77
  try {
75
- await fs.mkdir(dirname(this.cachePath), { recursive: true });
78
+ await fs_1.promises.mkdir((0, path_1.dirname)(this.cachePath), { recursive: true });
76
79
  const slim = items.map((item) => {
77
80
  const picked = {};
78
81
  for (const key of CACHED_FIELDS)
@@ -80,20 +83,20 @@ export class BangumiDataLoader {
80
83
  picked[key] = item[key];
81
84
  return picked;
82
85
  });
83
- await fs.writeFile(temp, JSON.stringify({ items: slim }), 'utf8');
84
- await fs.rename(temp, this.cachePath);
86
+ await fs_1.promises.writeFile(temp, JSON.stringify({ items: slim }), 'utf8');
87
+ await fs_1.promises.rename(temp, this.cachePath);
85
88
  }
86
89
  catch (error) {
87
- await fs.rm(temp, { force: true }).catch(() => undefined);
88
- this.logger.debug('[index] 写入缓存失败: %s', errorMessage(error));
90
+ await fs_1.promises.rm(temp, { force: true }).catch(() => undefined);
91
+ this.logger.debug('[index] 写入缓存失败: %s', (0, utils_1.errorMessage)(error));
89
92
  }
90
93
  }
91
94
  /** CDN 镜像回退(GitHub raw 在部分网络下不可达) */
92
95
  async loadFromNetwork() {
93
- for (const url of BANGUMI_DATA_FALLBACK_URLS) {
96
+ for (const url of constants_1.BANGUMI_DATA_FALLBACK_URLS) {
94
97
  try {
95
98
  const raw = await this.http.get(url, { timeout: 60000, responseType: 'text' });
96
- const text = unwrapText(raw);
99
+ const text = (0, utils_1.unwrapText)(raw);
97
100
  if (!text)
98
101
  continue;
99
102
  const parsed = JSON.parse(text);
@@ -101,10 +104,11 @@ export class BangumiDataLoader {
101
104
  return parsed.items;
102
105
  }
103
106
  catch (error) {
104
- this.logger.debug('[index] 镜像 %s 获取失败: %s', url, errorMessage(error));
107
+ this.logger.debug('[index] 镜像 %s 获取失败: %s', url, (0, utils_1.errorMessage)(error));
105
108
  this.http.hintOnFailure(error);
106
109
  }
107
110
  }
108
111
  return null;
109
112
  }
110
113
  }
114
+ exports.BangumiDataLoader = BangumiDataLoader;