cc-discord-framework-plugin 1.0.0
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/LICENSE +21 -0
- package/README.md +2 -0
- package/dist/SoundCloudPlugin/SoundCloudPlugin.d.ts +19 -0
- package/dist/SoundCloudPlugin/SoundCloudPlugin.js +55 -0
- package/dist/SoundCloudPlugin/index.d.ts +1 -0
- package/dist/SoundCloudPlugin/index.js +1 -0
- package/dist/VoiceVoxPlugin/VoiceVoxPlugin.d.ts +23 -0
- package/dist/VoiceVoxPlugin/VoiceVoxPlugin.js +68 -0
- package/dist/VoiceVoxPlugin/index.d.ts +1 -0
- package/dist/VoiceVoxPlugin/index.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yuya Nakayama
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type AudioResource } from "@discordjs/voice";
|
|
2
|
+
import type { QueuePlugin } from "cc-discord-framework/modules";
|
|
3
|
+
import { SoundCloudTrack } from "play-dl";
|
|
4
|
+
interface SearchCallbacks {
|
|
5
|
+
notFound?: () => void;
|
|
6
|
+
choice: (results: SoundCloudTrack[]) => Promise<string> | string | null;
|
|
7
|
+
}
|
|
8
|
+
export declare class SoundCloudPlugin implements QueuePlugin {
|
|
9
|
+
nowTrack: SoundCloudTrack | null;
|
|
10
|
+
private static isReady;
|
|
11
|
+
private url;
|
|
12
|
+
constructor();
|
|
13
|
+
private setup;
|
|
14
|
+
search(query: string, options: {
|
|
15
|
+
limit: number;
|
|
16
|
+
} | undefined, callbacks: SearchCallbacks): Promise<SoundCloudPlugin | null>;
|
|
17
|
+
createResource(): Promise<AudioResource>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createAudioResource as CAR } from "@discordjs/voice";
|
|
2
|
+
import { Logger } from "cc-discord-framework";
|
|
3
|
+
import playDl, { SoundCloudTrack } from "play-dl";
|
|
4
|
+
export class SoundCloudPlugin {
|
|
5
|
+
nowTrack = null;
|
|
6
|
+
static isReady = false;
|
|
7
|
+
url = null;
|
|
8
|
+
constructor() {
|
|
9
|
+
this.setup();
|
|
10
|
+
}
|
|
11
|
+
// 準備
|
|
12
|
+
async setup() {
|
|
13
|
+
if (!SoundCloudPlugin.isReady) {
|
|
14
|
+
const client_id = await playDl.getFreeClientID();
|
|
15
|
+
await playDl.setToken({
|
|
16
|
+
soundcloud: { client_id }
|
|
17
|
+
});
|
|
18
|
+
SoundCloudPlugin.isReady = true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// 検索
|
|
22
|
+
async search(query, options = { limit: 5 }, callbacks) {
|
|
23
|
+
await this.setup();
|
|
24
|
+
const results = await playDl.search(query, {
|
|
25
|
+
source: { soundcloud: "tracks" },
|
|
26
|
+
limit: options.limit
|
|
27
|
+
});
|
|
28
|
+
// 検索ヒットなし
|
|
29
|
+
if (!results || results.length === 0) {
|
|
30
|
+
callbacks.notFound?.();
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
// urlを選択させる
|
|
34
|
+
this.url = await callbacks.choice(results);
|
|
35
|
+
if (!this.url)
|
|
36
|
+
return null;
|
|
37
|
+
this.nowTrack = results.find(t => t.url === this.url) || null;
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
// 音源データ作成
|
|
41
|
+
async createResource() {
|
|
42
|
+
if (!this.url) {
|
|
43
|
+
Logger.error("[SoundCloudPlugin] URLが設定されていません");
|
|
44
|
+
throw new Error("URL_NOT_SET");
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const streamData = await playDl.stream(this.url);
|
|
48
|
+
return CAR(streamData.stream, { inputType: streamData.type });
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
Logger.error(error);
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./SoundCloudPlugin.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./SoundCloudPlugin.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { QueuePlugin } from "cc-discord-framework/modules";
|
|
2
|
+
import { type AudioResource } from "@discordjs/voice";
|
|
3
|
+
export interface GenerateOptions {
|
|
4
|
+
text: string;
|
|
5
|
+
speakerId: number;
|
|
6
|
+
speed?: number;
|
|
7
|
+
pitch?: number;
|
|
8
|
+
intonation?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class VoiceVoxPlugin implements QueuePlugin {
|
|
11
|
+
private readonly baseUrl;
|
|
12
|
+
private readonly cacheKey;
|
|
13
|
+
private guildId;
|
|
14
|
+
private audioBuffer;
|
|
15
|
+
constructor(options?: {
|
|
16
|
+
baseUrl?: string;
|
|
17
|
+
});
|
|
18
|
+
registerReadPlace(guildId: string, channelId: string): void;
|
|
19
|
+
getReadPlace(guildId: string): string | undefined;
|
|
20
|
+
generate(options: GenerateOptions): Promise<this>;
|
|
21
|
+
createResource(): Promise<AudioResource>;
|
|
22
|
+
destroy(): Promise<void>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Logger, Cache } from "cc-discord-framework";
|
|
2
|
+
import { createAudioResource as CAR, StreamType } from "@discordjs/voice";
|
|
3
|
+
import { Readable } from "stream";
|
|
4
|
+
import axios from "axios";
|
|
5
|
+
export class VoiceVoxPlugin {
|
|
6
|
+
baseUrl;
|
|
7
|
+
cacheKey = "VoiceVoxCache";
|
|
8
|
+
guildId = null;
|
|
9
|
+
audioBuffer = null;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.baseUrl = options?.baseUrl ?? "http://voicevox:50021";
|
|
12
|
+
}
|
|
13
|
+
// 読み上げ場所登録
|
|
14
|
+
registerReadPlace(guildId, channelId) {
|
|
15
|
+
this.guildId = guildId;
|
|
16
|
+
Cache.set(Cache.key(this.cacheKey, guildId), channelId);
|
|
17
|
+
}
|
|
18
|
+
// 読み上げ場所取得
|
|
19
|
+
getReadPlace(guildId) {
|
|
20
|
+
return Cache.get(Cache.key(this.cacheKey, guildId));
|
|
21
|
+
}
|
|
22
|
+
// 読み上げ素材を生成
|
|
23
|
+
async generate(options) {
|
|
24
|
+
try {
|
|
25
|
+
const queryRes = await axios.post(`${this.baseUrl}/audio_query`, null, {
|
|
26
|
+
params: { text: options.text, speaker: options.speakerId }
|
|
27
|
+
});
|
|
28
|
+
const queryData = queryRes.data;
|
|
29
|
+
if (options.speed)
|
|
30
|
+
queryData.speedScale = options.speed;
|
|
31
|
+
if (options.pitch)
|
|
32
|
+
queryData.pitchScale = options.pitch;
|
|
33
|
+
if (options.intonation)
|
|
34
|
+
queryData.intonationScale = options.intonation;
|
|
35
|
+
const synthRes = await axios.post(`${this.baseUrl}/synthesis`, queryData, {
|
|
36
|
+
params: { speaker: options.speakerId },
|
|
37
|
+
responseType: "arraybuffer"
|
|
38
|
+
});
|
|
39
|
+
this.audioBuffer = Buffer.from(synthRes.data);
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
Logger.error(error);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// 音源データ作成
|
|
48
|
+
async createResource() {
|
|
49
|
+
if (!this.audioBuffer) {
|
|
50
|
+
Logger.error("[VoiceVoxPlugin] 音声データが生成されていません");
|
|
51
|
+
throw new Error("BUFFER_NOT_SET");
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const stream = Readable.from(this.audioBuffer);
|
|
55
|
+
return CAR(stream, { inputType: StreamType.Arbitrary });
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
Logger.error(error);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// 解放時
|
|
63
|
+
async destroy() {
|
|
64
|
+
if (this.guildId) {
|
|
65
|
+
Cache.delete(Cache.key(this.cacheKey, this.guildId));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./VoiceVoxPlugin.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./VoiceVoxPlugin.js";
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cc-discord-framework-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/CHACCHAN/cc-discord-framework-plugin.git"
|
|
8
|
+
},
|
|
9
|
+
"description": "cc-discord-framework official plugins",
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"private": false,
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"package.json"
|
|
17
|
+
],
|
|
18
|
+
"module": "src/index.ts",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"require": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.build.json",
|
|
29
|
+
"clean": "rm -rf dist",
|
|
30
|
+
"prepublishOnly": "bun run clean && bun run build"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"cc-discord-framework": "latest",
|
|
34
|
+
"@discordjs/voice": "^0.19.2",
|
|
35
|
+
"play-dl": "^1.9.7",
|
|
36
|
+
"axios": "^1.16.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/bun": "latest",
|
|
40
|
+
"typescript": "^6.0.3"
|
|
41
|
+
}
|
|
42
|
+
}
|