koishi-plugin-vrchat 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.
package/lib/index.d.ts CHANGED
@@ -1,4 +1,9 @@
1
1
  import { Context, Schema } from 'koishi';
2
+ declare module '@koishijs/cache' {
3
+ interface Tables {
4
+ vrchat_auth: string;
5
+ }
6
+ }
2
7
  export declare const name = "vrchat";
3
8
  export declare const inject: string[];
4
9
  export interface Config {
package/lib/index.js CHANGED
@@ -28,10 +28,19 @@ __export(index_exports, {
28
28
  module.exports = __toCommonJS(index_exports);
29
29
  var import_koishi = require("koishi");
30
30
  var name = "vrchat";
31
- var inject = ["http"];
31
+ var inject = ["http", "cache"];
32
32
  var Config = import_koishi.Schema.object({});
33
+ function countryCodeToEmoji(code) {
34
+ if (!code || code.length !== 2) return "";
35
+ const A = 127462;
36
+ const offset = "A".charCodeAt(0);
37
+ const chars = code.toUpperCase().split("");
38
+ const first = A + (chars[0].charCodeAt(0) - offset);
39
+ const second = A + (chars[1].charCodeAt(0) - offset);
40
+ return String.fromCodePoint(first) + String.fromCodePoint(second);
41
+ }
42
+ __name(countryCodeToEmoji, "countryCodeToEmoji");
33
43
  function apply(ctx, config) {
34
- let auth;
35
44
  ctx.command("vrchat-login", "登录 VRChat API").action(async ({ session }) => {
36
45
  if (!session.isDirect) return "请通过私聊进行登录";
37
46
  await session.send("请输入用户名或邮箱地址:");
@@ -56,24 +65,28 @@ function apply(ctx, config) {
56
65
  await session.send("请输入发送到邮箱的验证码:");
57
66
  const code = await session.prompt();
58
67
  if (!code) return "输入超时。";
59
- const cookie = authResp.headers.get("set-cookie").split("; ")[0];
68
+ const cookie = authResp.headers.get("set-cookie").split("; ");
60
69
  const emailOtpResp = await ctx.http.post("https://api.vrchat.cloud/api/1/auth/twofactorauth/emailotp/verify", { code }, {
61
70
  headers: {
62
71
  "User-Agent": "VRCX 2026.02.11",
63
- "Cookie": cookie
72
+ "Cookie": cookie[0]
64
73
  },
65
74
  responseType: "json",
66
75
  validateStatus: /* @__PURE__ */ __name((status) => status < 500, "validateStatus")
67
76
  });
68
77
  if (emailOtpResp.verified) {
69
- auth = cookie;
78
+ const expires = new Date(cookie[3].split("=")[1]);
79
+ await ctx.cache.set("vrchat_auth", "cookie", cookie[0], expires.getTime() - Date.now());
70
80
  return "登录成功";
81
+ } else if (emailOtpResp.error) {
82
+ return emailOtpResp.error.message;
71
83
  }
72
84
  } else {
73
85
  ctx.logger.info(authResp);
74
86
  }
75
87
  });
76
88
  ctx.command("vrchat-avatars <keyword:text>", "检索 VRChat 模型").option("number", "-n <value:number>", { fallback: 10 }).action(async ({ session, options }, keyword) => {
89
+ const auth = await ctx.cache.get("vrchat_auth", "cookie");
77
90
  if (!auth) return "请先登录";
78
91
  if (!keyword) return "请输入关键词";
79
92
  const [msgId] = await session.send("检索中…");
@@ -94,6 +107,10 @@ function apply(ctx, config) {
94
107
  } catch {
95
108
  }
96
109
  }
110
+ if (avatarlist.length === 0) {
111
+ await session.bot.deleteMessage(session.channelId, msgId);
112
+ return "无检索结果";
113
+ }
97
114
  await session.send(`<message forward>${avatarlist.map(
98
115
  (e) => `<message>模型名:
99
116
  ${e.name}
@@ -119,6 +136,7 @@ ${new Date(e.updated_at).toLocaleString()}<img src="${e.imageUrl}"></img></messa
119
136
  await session.bot.deleteMessage(session.channelId, msgId);
120
137
  });
121
138
  ctx.command("vrchat-worlds <keyword:text>", "检索 VRChat 世界").option("number", "-n <value:number>", { fallback: 10 }).action(async ({ session, options }, keyword) => {
139
+ const auth = await ctx.cache.get("vrchat_auth", "cookie");
122
140
  if (!auth) return "请先登录";
123
141
  if (!keyword) return "请输入关键词";
124
142
  const [msgId] = await session.send("检索中…");
@@ -129,6 +147,10 @@ ${new Date(e.updated_at).toLocaleString()}<img src="${e.imageUrl}"></img></messa
129
147
  },
130
148
  responseType: "json"
131
149
  });
150
+ if (resp.length === 0) {
151
+ await session.bot.deleteMessage(session.channelId, msgId);
152
+ return "无检索结果";
153
+ }
132
154
  const messages = [];
133
155
  for (const item of resp) {
134
156
  let tags = [];
@@ -159,12 +181,13 @@ ${item.favorites}
159
181
  ${new Date(item.created_at).toLocaleString()}
160
182
 
161
183
  最后更新时间:
162
- ${new Date(item.updated_at).toLocaleString()}<img src="${item.imageUrl}"></img></message>`);
184
+ ${new Date(item.updated_at).toLocaleString()}<img src="${item.thumbnailImageUrl}"></img></message>`);
163
185
  }
164
186
  await session.send(`<message forward>${messages.join("")}</message>`);
165
187
  await session.bot.deleteMessage(session.channelId, msgId);
166
188
  });
167
189
  ctx.command("vrchat-users <keyword:text>", "检索 VRChat 玩家").option("number", "-n <value:number>", { fallback: 3 }).action(async ({ session, options }, keyword) => {
190
+ const auth = await ctx.cache.get("vrchat_auth", "cookie");
168
191
  if (!auth) return "请先登录";
169
192
  if (!keyword) return "请输入关键词";
170
193
  const [msgId] = await session.send("检索中…");
@@ -175,6 +198,10 @@ ${new Date(item.updated_at).toLocaleString()}<img src="${item.imageUrl}"></img><
175
198
  },
176
199
  responseType: "json"
177
200
  });
201
+ if (resp.length === 0) {
202
+ await session.bot.deleteMessage(session.channelId, msgId);
203
+ return "无检索结果";
204
+ }
178
205
  const users = [];
179
206
  for (const item of resp) {
180
207
  const resp2 = await ctx.http.get(`https://api.vrchat.cloud/api/1/users/${item.id}`, {
@@ -189,26 +216,42 @@ ${new Date(item.updated_at).toLocaleString()}<img src="${item.imageUrl}"></img><
189
216
  const messages = [];
190
217
  for (const item of users) {
191
218
  let avatar = "";
219
+ let currentAvatarImageUrl = item.currentAvatarImageUrl;
192
220
  if (item.currentAvatarImageUrl.startsWith("https://api.vrchat.cloud")) {
193
- const resp2 = await ctx.http.get(item.currentAvatarImageUrl.slice(0, -7), {
194
- headers: {
195
- "User-Agent": "VRCX 2026.02.11"
196
- },
197
- responseType: "json"
198
- });
199
- avatar = resp2.name.split(" - ")[1];
221
+ try {
222
+ const resp2 = await ctx.http.get(item.currentAvatarImageUrl.slice(0, -7), {
223
+ headers: {
224
+ "User-Agent": "VRCX 2026.02.11"
225
+ },
226
+ responseType: "json"
227
+ });
228
+ avatar = resp2.name.split(" - ")[1];
229
+ } catch {
230
+ currentAvatarImageUrl = void 0;
231
+ }
200
232
  }
201
233
  let location = item.location;
202
234
  if (item.location.startsWith("wrld_")) {
203
- const resp2 = await ctx.http.get(`https://api.vrchat.cloud/api/1/worlds/${item.location.split(":")[0]}`, {
235
+ const info = item.location.split(":");
236
+ const resp2 = await ctx.http.get(`https://api.vrchat.cloud/api/1/worlds/${info[0]}`, {
204
237
  headers: {
205
238
  "User-Agent": "VRCX 2026.02.11",
206
239
  "Cookie": auth
207
240
  },
208
241
  responseType: "json"
209
242
  });
210
- location = resp2.name;
243
+ const ext = info[1].split("~");
244
+ location = `${resp2.name} #${ext[0]} ${countryCodeToEmoji(ext.at(-1).match(/region\(([^)]+)\)/)[1])}`;
211
245
  }
246
+ const statusLight = {
247
+ "active": "🟢",
248
+ "join me": "🔵",
249
+ "ask me": "🟠",
250
+ "busy": "🔴",
251
+ "offline": "⚪"
252
+ }[item.status];
253
+ let imgUrl = item.userIcon || currentAvatarImageUrl;
254
+ const img = imgUrl ? `<img src="${imgUrl}"></img>` : "";
212
255
  messages.push(`<message>玩家名:
213
256
  ${item.displayName}
214
257
 
@@ -216,7 +259,7 @@ ${item.displayName}
216
259
  ${item.id}
217
260
 
218
261
  状态:
219
- ${item.status} - ${item.statusDescription}
262
+ ${statusLight} ${item.status} - ${item.statusDescription}
220
263
 
221
264
  当前位置:
222
265
  ${location}
@@ -234,7 +277,7 @@ ${item.bio}
234
277
  ${item.bioLinks.join("\n")}
235
278
 
236
279
  账号创建日期:
237
- ${item.date_joined}<img src="${item.userIcon || item.currentAvatarImageUrl}"></img></message>`);
280
+ ${item.date_joined}${img}</message>`);
238
281
  }
239
282
  await session.send(`<message forward>${messages.join("")}</message>`);
240
283
  await session.bot.deleteMessage(session.channelId, msgId);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-vrchat",
3
- "description": "Search VRChat Avatars and Worlds",
4
- "version": "0.0.2",
3
+ "description": "Search VRChat avatars, worlds, and users",
4
+ "version": "0.0.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
@@ -19,14 +19,17 @@
19
19
  "peerDependencies": {
20
20
  "koishi": "^4.17.7"
21
21
  },
22
+ "devDependencies": {
23
+ "@koishijs/cache": "^2.1.0"
24
+ },
22
25
  "repository": {
23
26
  "type": "git",
24
27
  "url": "https://github.com/idranme/koishi-plugin-vrchat.git"
25
28
  },
26
29
  "koishi": {
27
30
  "description": {
28
- "zh": "检索 VRChat 模型与世界",
29
- "en": "Search VRChat Avatars and Worlds"
31
+ "zh": "检索 VRChat 模型、世界和玩家",
32
+ "en": "Search VRChat avatars, worlds, and users"
30
33
  },
31
34
  "browser": true
32
35
  }
package/readme.md CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/koishi-plugin-vrchat?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-vrchat)
4
4
 
5
- Search VRChat Avatars and Worlds
5
+ Search VRChat avatars, worlds, and users