koishi-plugin-apple-rank 0.0.2 → 0.0.4

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.
Files changed (2) hide show
  1. package/lib/index.js +81 -31
  2. package/package.json +4 -1
package/lib/index.js CHANGED
@@ -25,6 +25,7 @@ __export(src_exports, {
25
25
  name: () => name
26
26
  });
27
27
  module.exports = __toCommonJS(src_exports);
28
+ var import_jimp = require("jimp");
28
29
  var import_koishi = require("koishi");
29
30
  var name = "apple-rank";
30
31
  var Config = import_koishi.Schema.object({
@@ -80,7 +81,7 @@ function formatRankItem(item) {
80
81
  __name(formatRankItem, "formatRankItem");
81
82
  function formatRankItemWithIcon(item) {
82
83
  const text = formatRankItem(item);
83
- return item.icon ? `${import_koishi.h.image(item.icon)}${text}` : text;
84
+ return item.icon ? [import_koishi.h.image(item.icon), text].join("") : text;
84
85
  }
85
86
  __name(formatRankItemWithIcon, "formatRankItemWithIcon");
86
87
  function formatUpdatedAt(updated) {
@@ -90,6 +91,14 @@ function formatUpdatedAt(updated) {
90
91
  return `更新时间:${date.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai", hour12: false })}`;
91
92
  }
92
93
  __name(formatUpdatedAt, "formatUpdatedAt");
94
+ function formatNote() {
95
+ return "说明:Apple 公开榜单,只代表 iOS 排名,不代表全平台流水";
96
+ }
97
+ __name(formatNote, "formatNote");
98
+ function joinBlocks(blocks) {
99
+ return blocks.filter(Boolean).join("\n\n");
100
+ }
101
+ __name(joinBlocks, "joinBlocks");
93
102
  function parseFeedType(type = "grossing") {
94
103
  if (["grossing", "畅销", "畅销榜", "流水"].includes(type)) return "topgrossingapplications";
95
104
  if (["free", "免费", "免费榜"].includes(type)) return "topfreeapplications";
@@ -106,6 +115,7 @@ function apply(ctx, config) {
106
115
  let cachedFeed = "";
107
116
  let cachedRankFeed = { ranks: [], actualLimit: 0 };
108
117
  const recentReplies = /* @__PURE__ */ new Map();
118
+ const grayscaleIconCache = /* @__PURE__ */ new Map();
109
119
  async function fetchRanks(feedType = "topgrossingapplications") {
110
120
  const now = Date.now();
111
121
  if (cachedFeed === feedType && cachedRankFeed.ranks.length && now - cachedAt < config.cacheTtl * 1e3) {
@@ -136,6 +146,21 @@ function apply(ctx, config) {
136
146
  return true;
137
147
  }
138
148
  __name(shouldReplyOnce, "shouldReplyOnce");
149
+ async function toGrayscaleIcon(url) {
150
+ if (!url) return void 0;
151
+ const cached = grayscaleIconCache.get(url);
152
+ if (cached) return cached;
153
+ try {
154
+ const image = await import_jimp.Jimp.read(url);
155
+ image.greyscale();
156
+ const base64 = await image.getBase64("image/png");
157
+ grayscaleIconCache.set(url, base64);
158
+ return base64;
159
+ } catch {
160
+ return url;
161
+ }
162
+ }
163
+ __name(toGrayscaleIcon, "toGrayscaleIcon");
139
164
  async function searchApp(keyword) {
140
165
  const url = "https://itunes.apple.com/search";
141
166
  const response = parseJson(await ctx.http.get(url, {
@@ -187,7 +212,14 @@ function apply(ctx, config) {
187
212
  const feed = await fetchRanks(feedType);
188
213
  const actualLimit = feed.actualLimit || feed.ranks.length;
189
214
  const lines = feed.ranks.slice(0, Math.min(limit, actualLimit)).map(formatRankItemWithIcon);
190
- return [`App Store ${config.country.toUpperCase()} ${feedLabels[feedType]} Top ${actualLimit}`, formatUpdatedAt(feed.updated), ...lines].join("\n");
215
+ return joinBlocks([
216
+ `App Store ${config.country.toUpperCase()} ${feedLabels[feedType]} Top ${actualLimit}`,
217
+ [
218
+ formatUpdatedAt(feed.updated),
219
+ formatNote()
220
+ ].join("\n"),
221
+ lines.join("\n")
222
+ ]);
191
223
  });
192
224
  ctx.command("ios排名 <game:text>", "查询游戏在 iOS 畅销榜的排名").option("type", "-t <type:string> 榜单类型:畅销/免费/付费", { fallback: "畅销" }).action(async ({ options, session }, game) => {
193
225
  if (session && !shouldReplyOnce(`${session.sid}:ios排名:${options.type}:${game}`)) return;
@@ -197,21 +229,29 @@ function apply(ctx, config) {
197
229
  const { appId, item, detail, updated, actualLimit } = await findRank(game, feedType);
198
230
  if (!appId) return `没找到「${game}」对应的 App Store 应用`;
199
231
  if (!item) {
200
- return [
232
+ return joinBlocks([
201
233
  detail?.artworkUrl100 ? import_koishi.h.image(detail.artworkUrl100) : "",
202
- `「${detail?.trackName || game}」未进入 App Store ${config.country.toUpperCase()} ${feedLabels[feedType]} Top ${actualLimit}`,
203
- formatUpdatedAt(updated),
204
- "口径:Apple 公开榜单,只代表 iOS 排名,不代表全平台流水"
205
- ].filter(Boolean).join("\n");
234
+ `${detail?.trackName || game}`,
235
+ `未进入 App Store ${config.country.toUpperCase()} ${feedLabels[feedType]} Top ${actualLimit}`,
236
+ [
237
+ formatUpdatedAt(updated),
238
+ detail?.artistName ? `开发者:${detail.artistName}` : "",
239
+ detail?.primaryGenreName ? `分类:${detail.genres?.join(" / ") || detail.primaryGenreName}` : "",
240
+ formatNote()
241
+ ].filter(Boolean).join("\n")
242
+ ]);
206
243
  }
207
- return [
244
+ return joinBlocks([
208
245
  item.icon ? import_koishi.h.image(item.icon) : "",
209
- `${item.name} iOS ${feedLabels[feedType]}排名:第 ${item.rank} 名`,
210
- formatUpdatedAt(updated),
211
- `开发者:${item.artist}`,
212
- `分类:${item.category || "未知"}`,
213
- "口径:Apple 公开榜单,只代表 iOS 排名,不代表全平台流水"
214
- ].filter(Boolean).join("\n");
246
+ `${item.name}`,
247
+ [
248
+ `排名:App Store ${config.country.toUpperCase()} ${feedLabels[feedType]} 第 ${item.rank} 名`,
249
+ formatUpdatedAt(updated),
250
+ `开发者:${item.artist}`,
251
+ `分类:${item.category || "未知"}`,
252
+ formatNote()
253
+ ].join("\n")
254
+ ]);
215
255
  });
216
256
  ctx.command("ios对比 <games:text>", "对比多个游戏的 iOS 畅销榜排名").action(async ({ session }, games) => {
217
257
  if (session && !shouldReplyOnce(`${session.sid}:ios对比:${games}`)) return;
@@ -220,28 +260,35 @@ function apply(ctx, config) {
220
260
  const feed = await fetchRanks();
221
261
  const actualLimit = feed.actualLimit || feed.ranks.length;
222
262
  const details = await Promise.all(names.map(resolveAppDetail));
263
+ const grayIcons = await Promise.all(details.map((detail) => toGrayscaleIcon(detail?.artworkUrl100)));
223
264
  const results = names.map((game, index) => {
224
265
  const detail = details[index];
266
+ const grayIcon = grayIcons[index];
225
267
  const appId = detail?.trackId.toString() || config.aliases[game] || (/^\d+$/.test(game) ? game : void 0);
226
268
  if (!appId) return `${game}:未找到应用`;
227
269
  const item = feed.ranks.find((item2) => item2.appId === appId);
228
270
  if (!item) {
229
- return [
230
- detail?.artworkUrl100 ? import_koishi.h.image(detail.artworkUrl100) : "",
231
- `${detail?.trackName || game}:未进入畅销榜 Top ${actualLimit}`
232
- ].filter(Boolean).join("\n");
271
+ return joinBlocks([
272
+ grayIcon ? import_koishi.h.image(grayIcon) : "",
273
+ `${detail?.trackName || game}`,
274
+ `未进入畅销榜 Top ${actualLimit}`
275
+ ]);
233
276
  }
234
- return [
277
+ return joinBlocks([
235
278
  item.icon ? import_koishi.h.image(item.icon) : "",
236
- `${item.name}:第 ${item.rank} 名`
237
- ].filter(Boolean).join("\n");
279
+ `${item.name}`,
280
+ `排名:第 ${item.rank} 名`
281
+ ]);
238
282
  });
239
- return [
283
+ return joinBlocks([
240
284
  `App Store ${config.country.toUpperCase()} 畅销榜对比`,
241
- formatUpdatedAt(feed.updated),
242
- ...results,
243
- "口径:Apple 公开榜单,只代表 iOS 排名,不代表全平台流水"
244
- ].join("\n");
285
+ [
286
+ formatUpdatedAt(feed.updated),
287
+ `榜单条数:${actualLimit}`,
288
+ formatNote()
289
+ ].join("\n"),
290
+ results.join("\n\n")
291
+ ]);
245
292
  });
246
293
  ctx.command("ios搜索 <keyword:text>", "搜索 App Store 应用").action(async ({ session }, keyword) => {
247
294
  if (session && !shouldReplyOnce(`${session.sid}:ios搜索:${keyword}`)) return;
@@ -251,12 +298,15 @@ function apply(ctx, config) {
251
298
  return results.map((result, index) => {
252
299
  const genres = result.genres?.join(" / ") || result.primaryGenreName;
253
300
  const icon = result.artworkUrl100 ? `${import_koishi.h.image(result.artworkUrl100)}` : "";
254
- return [
301
+ return joinBlocks([
255
302
  icon,
256
- `${index + 1}. ${result.trackName} (${result.trackId})`,
257
- `开发者:${result.artistName}`,
258
- `分类:${genres}`
259
- ].filter(Boolean).join("\n");
303
+ `${index + 1}. ${result.trackName}`,
304
+ [
305
+ `App ID:${result.trackId}`,
306
+ `开发者:${result.artistName}`,
307
+ `分类:${genres}`
308
+ ].join("\n")
309
+ ]);
260
310
  }).join("\n\n");
261
311
  });
262
312
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-apple-rank",
3
3
  "description": "给二游痴看流水用的,数据来自苹果的rss",
4
- "version": "0.0.2",
4
+ "version": "0.0.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -14,6 +14,9 @@
14
14
  "koishi",
15
15
  "plugin"
16
16
  ],
17
+ "dependencies": {
18
+ "jimp": "^1.6.0"
19
+ },
17
20
  "peerDependencies": {
18
21
  "koishi": "^4.18.7"
19
22
  }