opencrush 0.3.8 → 0.3.10

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/dist/index.js +70 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -234588,6 +234588,21 @@ function debugLog3(msg) {
234588
234588
  } catch {
234589
234589
  }
234590
234590
  }
234591
+ async function loadVoice() {
234592
+ if (voice) return voice;
234593
+ if (voiceLoadError) return null;
234594
+ try {
234595
+ voice = await import("@discordjs/voice");
234596
+ return voice;
234597
+ } catch (err) {
234598
+ voiceLoadError = err instanceof Error ? err.message : String(err);
234599
+ console.warn(`[Discord] @discordjs/voice not available \u2014 voice features disabled (${voiceLoadError})`);
234600
+ return null;
234601
+ }
234602
+ }
234603
+ function getVoiceSync() {
234604
+ return voice;
234605
+ }
234591
234606
  async function loadPrism() {
234592
234607
  if (!prism) {
234593
234608
  prism = await import("prism-media");
@@ -234610,15 +234625,16 @@ function splitMessage(text, maxLength = 1900) {
234610
234625
  if (current) chunks.push(current.trim());
234611
234626
  return chunks;
234612
234627
  }
234613
- var import_discord, import_fs15, import_voice, import_stream4, DEBUG_LOG, prism, DiscordBridge;
234628
+ var import_discord, import_fs15, import_stream4, DEBUG_LOG, voice, voiceLoadError, prism, DiscordBridge;
234614
234629
  var init_dist5 = __esm({
234615
234630
  "../bridges/discord/dist/index.mjs"() {
234616
234631
  "use strict";
234617
234632
  import_discord = __toESM(require_src3(), 1);
234618
234633
  import_fs15 = require("fs");
234619
- import_voice = require("@discordjs/voice");
234620
234634
  import_stream4 = require("stream");
234621
234635
  DEBUG_LOG = "/tmp/opencrush-debug.log";
234636
+ voice = null;
234637
+ voiceLoadError = null;
234622
234638
  prism = null;
234623
234639
  DiscordBridge = class {
234624
234640
  client;
@@ -234839,6 +234855,8 @@ var init_dist5 = __esm({
234839
234855
  }
234840
234856
  async handleVoiceState(oldState, newState) {
234841
234857
  var _a3;
234858
+ const v2 = await loadVoice();
234859
+ if (!v2) return;
234842
234860
  if (((_a3 = newState.member) == null ? void 0 : _a3.id) !== this.config.ownerId) return;
234843
234861
  const userJoinedVoice = !oldState.channelId && newState.channelId;
234844
234862
  const userLeftVoice = oldState.channelId && !newState.channelId;
@@ -234868,7 +234886,9 @@ var init_dist5 = __esm({
234868
234886
  * Creates a loop: listen → transcribe → LLM → TTS → speak → repeat.
234869
234887
  */
234870
234888
  startVoiceConversation(guildId) {
234871
- const connection = (0, import_voice.getVoiceConnection)(guildId);
234889
+ const v2 = getVoiceSync();
234890
+ if (!v2) return;
234891
+ const connection = v2.getVoiceConnection(guildId);
234872
234892
  if (!connection) return;
234873
234893
  this.voiceConversationActive.set(guildId, true);
234874
234894
  console.log(`[Discord/Voice] Starting voice conversation in guild ${guildId}`);
@@ -234881,7 +234901,9 @@ var init_dist5 = __esm({
234881
234901
  }
234882
234902
  async listenForNextUtterance(guildId) {
234883
234903
  if (!this.voiceConversationActive.get(guildId)) return;
234884
- const connection = (0, import_voice.getVoiceConnection)(guildId);
234904
+ const v2 = getVoiceSync();
234905
+ if (!v2) return;
234906
+ const connection = v2.getVoiceConnection(guildId);
234885
234907
  if (!connection) return;
234886
234908
  if (this.isSpeaking.get(guildId)) {
234887
234909
  setTimeout(() => this.listenForNextUtterance(guildId), 500);
@@ -234891,7 +234913,7 @@ var init_dist5 = __esm({
234891
234913
  const receiver = connection.receiver;
234892
234914
  const opusStream = receiver.subscribe(this.config.ownerId, {
234893
234915
  end: {
234894
- behavior: import_voice.EndBehaviorType.AfterSilence,
234916
+ behavior: v2.EndBehaviorType.AfterSilence,
234895
234917
  duration: 1500
234896
234918
  }
234897
234919
  });
@@ -234990,36 +235012,42 @@ var init_dist5 = __esm({
234990
235012
  }
234991
235013
  // ── Voice Channel Management ───────────────────────────────
234992
235014
  async joinVoiceChannel(channelId, guildId, adapterCreator) {
235015
+ const v2 = getVoiceSync();
235016
+ if (!v2) return;
234993
235017
  try {
234994
- const connection = (0, import_voice.joinVoiceChannel)({
235018
+ const connection = v2.joinVoiceChannel({
234995
235019
  channelId,
234996
235020
  guildId,
234997
235021
  adapterCreator,
234998
235022
  selfDeaf: false,
234999
235023
  selfMute: false
235000
235024
  });
235001
- await (0, import_voice.entersState)(connection, import_voice.VoiceConnectionStatus.Ready, 3e4);
235025
+ await v2.entersState(connection, v2.VoiceConnectionStatus.Ready, 3e4);
235002
235026
  console.log(`[Discord] Joined voice channel ${channelId}`);
235003
235027
  } catch (err) {
235004
235028
  console.error("[Discord] Failed to join voice channel:", err);
235005
235029
  }
235006
235030
  }
235007
235031
  leaveVoiceChannel(guildId) {
235008
- const connection = (0, import_voice.getVoiceConnection)(guildId);
235032
+ const v2 = getVoiceSync();
235033
+ if (!v2) return;
235034
+ const connection = v2.getVoiceConnection(guildId);
235009
235035
  if (connection) {
235010
235036
  connection.destroy();
235011
235037
  console.log(`[Discord] Left voice channel in guild ${guildId}`);
235012
235038
  }
235013
235039
  }
235014
235040
  async playAudio(guildId, audioBuffer) {
235015
- const connection = (0, import_voice.getVoiceConnection)(guildId);
235041
+ const v2 = getVoiceSync();
235042
+ if (!v2) return;
235043
+ const connection = v2.getVoiceConnection(guildId);
235016
235044
  if (!connection) return;
235017
- const player = (0, import_voice.createAudioPlayer)();
235045
+ const player = v2.createAudioPlayer();
235018
235046
  const readable = import_stream4.Readable.from(audioBuffer);
235019
- const resource = (0, import_voice.createAudioResource)(readable);
235047
+ const resource = v2.createAudioResource(readable);
235020
235048
  player.play(resource);
235021
235049
  connection.subscribe(player);
235022
- await (0, import_voice.entersState)(player, import_voice.AudioPlayerStatus.Idle, 6e4);
235050
+ await v2.entersState(player, v2.AudioPlayerStatus.Idle, 6e4);
235023
235051
  }
235024
235052
  // ── Message Handling ───────────────────────────────────────
235025
235053
  /**
@@ -235215,6 +235243,7 @@ var init_dist5 = __esm({
235215
235243
  }
235216
235244
  }
235217
235245
  async start() {
235246
+ await loadVoice();
235218
235247
  await this.client.login(this.config.token);
235219
235248
  }
235220
235249
  async stop() {
@@ -236377,10 +236406,10 @@ var require_context = __commonJS({
236377
236406
  *
236378
236407
  * **Official reference:** https://core.telegram.org/bots/api#sendvoice
236379
236408
  */
236380
- replyWithVoice(voice, other, signal) {
236409
+ replyWithVoice(voice2, other, signal) {
236381
236410
  var _a3;
236382
236411
  const msg = this.msg;
236383
- return this.api.sendVoice(orThrow(this.chatId, "sendVoice"), voice, {
236412
+ return this.api.sendVoice(orThrow(this.chatId, "sendVoice"), voice2, {
236384
236413
  business_connection_id: this.businessConnectionId,
236385
236414
  ...(msg === null || msg === void 0 ? void 0 : msg.is_topic_message) ? { message_thread_id: msg.message_thread_id } : {},
236386
236415
  direct_messages_topic_id: (_a3 = msg === null || msg === void 0 ? void 0 : msg.direct_messages_topic) === null || _a3 === void 0 ? void 0 : _a3.topic_id,
@@ -239463,8 +239492,8 @@ var require_api3 = __commonJS({
239463
239492
  *
239464
239493
  * **Official reference:** https://core.telegram.org/bots/api#sendvoice
239465
239494
  */
239466
- sendVoice(chat_id, voice, other, signal) {
239467
- return this.raw.sendVoice({ chat_id, voice, ...other }, signal);
239495
+ sendVoice(chat_id, voice2, other, signal) {
239496
+ return this.raw.sendVoice({ chat_id, voice: voice2, ...other }, signal);
239468
239497
  }
239469
239498
  /**
239470
239499
  * Use this method to send video messages. On success, the sent Message is returned.
@@ -245004,7 +245033,7 @@ function getExistingCharacters() {
245004
245033
  const dir = getCharactersDir();
245005
245034
  if (!(0, import_fs17.existsSync)(dir)) return [];
245006
245035
  try {
245007
- return (0, import_fs17.readdirSync)(dir, { withFileTypes: true }).filter((d2) => d2.isDirectory() && d2.name !== "example").map((d2) => d2.name);
245036
+ return (0, import_fs17.readdirSync)(dir, { withFileTypes: true }).filter((d2) => d2.isDirectory() && d2.name !== "example").filter((d2) => (0, import_fs17.existsSync)((0, import_path11.join)(dir, d2.name, "IDENTITY.md"))).map((d2) => d2.name);
245008
245037
  } catch {
245009
245038
  return [];
245010
245039
  }
@@ -245090,16 +245119,17 @@ async function runSetupWizard() {
245090
245119
  console.log(source_default.gray(" These companions are ready to go:\n"));
245091
245120
  const choices = characters.map((c2) => {
245092
245121
  const info = getCharacterInfo(c2);
245093
- const meta = [info.age, info.location].filter(Boolean).join(" \xB7 ");
245122
+ const parts = [info.age, info.location].filter(Boolean);
245094
245123
  const desc = info.tagline || info.hobbies.join(", ");
245095
- const line2 = [meta, desc].filter(Boolean).join(" \u2014 ");
245124
+ if (desc) parts.push(desc);
245125
+ const preview = parts.length > 0 ? source_default.gray(" \xB7 " + parts.join(" \xB7 ")) : "";
245096
245126
  return {
245097
- name: `\u2728 ${c2}${line2 ? "\n " + source_default.gray(line2) : ""}`,
245127
+ name: `\u2728 ${c2}${preview}`,
245098
245128
  value: c2,
245099
245129
  short: c2
245100
245130
  };
245101
245131
  });
245102
- choices.push({ name: source_default.gray("\u2795 Create someone new"), value: "__new__", short: "New" });
245132
+ choices.push({ name: "\u2795 Create someone new", value: "__new__", short: "New" });
245103
245133
  const { pick } = await esm_default12.prompt([{
245104
245134
  type: "list",
245105
245135
  name: "pick",
@@ -245489,7 +245519,8 @@ function parseIdentity(identityPath) {
245489
245519
  const ageMatch = content.match(/\*\*Age:\*\*\s*(\d+)/i);
245490
245520
  const age = (ageMatch == null ? void 0 : ageMatch[1]) ?? "??";
245491
245521
  const fromMatch = content.match(/\*\*From:\*\*\s*(.+)/i);
245492
- const location = ((_b2 = fromMatch == null ? void 0 : fromMatch[1]) == null ? void 0 : _b2.trim()) ?? "Unknown";
245522
+ const locationRaw = ((_b2 = fromMatch == null ? void 0 : fromMatch[1]) == null ? void 0 : _b2.trim()) ?? "Unknown";
245523
+ const location = locationRaw.replace(/\s*\(.*\)\s*$/, "").trim();
245493
245524
  const hobbiesMatch = content.match(/\*\*Hobbies:\*\*\s*(.+)/i);
245494
245525
  const hobbiesRaw = (hobbiesMatch == null ? void 0 : hobbiesMatch[1]) ?? "";
245495
245526
  const tags = hobbiesRaw.split(",").map((t2) => t2.trim()).filter(Boolean).slice(0, 5);
@@ -245520,27 +245551,29 @@ function pickGradient(name) {
245520
245551
  function createSvgOverlay(data, gradient) {
245521
245552
  const textX = PORTRAIT_X + PORTRAIT_SIZE + 60;
245522
245553
  const tagY = 280;
245523
- const tagsSvg = data.tags.map((tag, i2) => {
245524
- const x2 = textX + i2 * 0;
245525
- const inlineX = textX;
245526
- const inlineY = tagY + i2 * 36;
245527
- return `
245528
- <rect x="${inlineX - 4}" y="${inlineY - 18}" width="${tag.length * 10 + 24}" height="28" rx="14" fill="rgba(255,255,255,0.12)" />
245529
- <text x="${inlineX + 8}" y="${inlineY}" font-family="system-ui, -apple-system, sans-serif" font-size="14" fill="#e0e0e0">${escapeXml(tag)}</text>
245530
- `;
245531
- }).join("\n");
245554
+ const maxTagX = WIDTH - 40;
245532
245555
  let inlineTags = "";
245533
245556
  let offsetX = textX;
245534
- const tagRowY = tagY;
245557
+ let rowY = tagY;
245558
+ const rowHeight = 34;
245559
+ const maxRows = 2;
245560
+ let currentRow = 1;
245535
245561
  for (const tag of data.tags) {
245536
- const w2 = tag.length * 8.5 + 24;
245562
+ const label = tag.length > 22 ? tag.slice(0, 21) + "\u2026" : tag;
245563
+ const w2 = label.length * 8.5 + 24;
245564
+ if (offsetX + w2 > maxTagX) {
245565
+ if (currentRow >= maxRows) break;
245566
+ currentRow++;
245567
+ offsetX = textX;
245568
+ rowY += rowHeight;
245569
+ }
245537
245570
  inlineTags += `
245538
- <rect x="${offsetX - 2}" y="${tagRowY - 16}" width="${w2}" height="26" rx="13" fill="rgba(255,255,255,0.10)" />
245539
- <text x="${offsetX + 10}" y="${tagRowY}" font-family="system-ui, -apple-system, sans-serif" font-size="13" fill="#c0c0c0">${escapeXml(tag)}</text>
245571
+ <rect x="${offsetX - 2}" y="${rowY - 16}" width="${w2}" height="26" rx="13" fill="rgba(255,255,255,0.10)" />
245572
+ <text x="${offsetX + 10}" y="${rowY}" font-family="system-ui, -apple-system, sans-serif" font-size="13" fill="#c0c0c0">${escapeXml(label)}</text>
245540
245573
  `;
245541
245574
  offsetX += w2 + 8;
245542
- if (offsetX > WIDTH - 40) break;
245543
245575
  }
245576
+ const descriptionY = tagY + currentRow * rowHeight + 16;
245544
245577
  return `<svg width="${WIDTH}" height="${HEIGHT}" xmlns="http://www.w3.org/2000/svg">
245545
245578
  <defs>
245546
245579
  <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
@@ -245568,7 +245601,7 @@ function createSvgOverlay(data, gradient) {
245568
245601
  ${inlineTags}
245569
245602
 
245570
245603
  <!-- One-line description -->
245571
- <text x="${textX}" y="${tagRowY + 50}" font-family="system-ui, -apple-system, sans-serif" font-size="15" fill="#b0b0b0" font-style="italic">${escapeXml(truncate(data.description, 70))}</text>
245604
+ <text x="${textX}" y="${descriptionY}" font-family="system-ui, -apple-system, sans-serif" font-size="15" fill="#b0b0b0" font-style="italic">${escapeXml(truncate(data.description, 70))}</text>
245572
245605
 
245573
245606
  <!-- Bottom bar -->
245574
245607
  <rect x="0" y="${HEIGHT - 50}" width="${WIDTH}" height="50" fill="rgba(0,0,0,0.3)" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencrush",
3
- "version": "0.3.8",
3
+ "version": "0.3.10",
4
4
  "description": "Your AI companion lives on your device. She watches dramas, listens to music, and thinks of you.",
5
5
  "bin": {
6
6
  "opencrush": "dist/index.js"