Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: additional settings #2280

Merged
merged 17 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/docs/guide/commands/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
|[キュー内を検索](./searchqueue.md)|キュー内を検索します。引数にキーワードを指定します。|
|[サウンドクラウドを検索](./searchsoundcloud.md)|曲をSoundCloudで検索します。|
|[シーク](./seek.md)|楽曲をシークします。|
|[設定>現在再生中](./setting>nowplaying.md)|現在再生中パネルの表示モードの設定をします。何も指定しないと現在の設定を確認できます。|
|[設定>スキップ投票](./setting>skipvote.md)|スキップ投票の有効・無効の設定をします。何も指定しないと現在の設定を確認できます。|
|[シャッフル](./shuffle.md)|キューの内容をシャッフルします。|
|[スキップ](./skip.md)|状況に応じて現在再生中の曲をスキップするか、スキップ投票を開始します。|
|[サムネイル](./thumbnail.md)|現在再生中のサムネイルを表示します。検索パネルが開いていて検索パネル中の番号が指定された場合にはその曲のサムネイルを表示します。|
Expand Down
21 changes: 21 additions & 0 deletions docs/docs/guide/commands/setting_nowplaying.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
sidebar_label: 設定>現在再生中
---
# `設定>現在再生中`コマンド
現在再生中パネルの表示モードの設定をします。何も指定しないと現在の設定を確認できます。

スラッシュコマンドでは、`/setting nowplaying`を使用してください。

## 別名
`設定>現在再生中`以外にも以下の別名を使用できます。

- setting>nowplaying




## 実行に必要な権限
同じボイスチャンネルに接続していてかつDJロールを保持していること, ボイスチャンネルの唯一のユーザーであること, サーバーの管理権限を持っていることのいずれか

※管理者権限や、サーバーの管理権限、チャンネルの管理権限、および管理者権限を持つユーザーはこの権限を満たしていなくてもいつでもこのコマンドを実行できます。

21 changes: 21 additions & 0 deletions docs/docs/guide/commands/setting_skipvote.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
sidebar_label: 設定>スキップ投票
---
# `設定>スキップ投票`コマンド
スキップ投票の有効・無効の設定をします。何も指定しないと現在の設定を確認できます。

スラッシュコマンドでは、`/setting skipvote`を使用してください。

## 別名
`設定>スキップ投票`以外にも以下の別名を使用できます。

- setting>skipvote




## 実行に必要な権限
同じボイスチャンネルに接続していてかつDJロールを保持していること, ボイスチャンネルの唯一のユーザーであること, サーバーの管理権限を持っていることのいずれか

※管理者権限や、サーバーの管理権限、チャンネルの管理権限、および管理者権限を持つユーザーはこの権限を満たしていなくてもいつでもこのコマンドを実行できます。

35 changes: 34 additions & 1 deletion locales/ja/commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"player": "音楽プレイヤー制御系",
"playlist": "プレイリスト操作系",
"utility": "ユーティリティ系",
"bot": "ボット操作全般"
"bot": "ボット操作全般",
"settings": "設定"
},
"commandList": "コマンド一覧",
"toLearnMoreMessage": "`{{prefix}}コマンド 再生`のように、コマンド名を引数につけて、そのコマンドの詳細を表示できます。",
Expand Down Expand Up @@ -489,6 +490,38 @@
"success": "シークしました",
"failed": "シークに失敗しました"
},
"setting": {
"name": "設定",
"description": "ボットの設定を変更します"
},
"setting>skipvote": {
"name": "設定>スキップ投票",
"description": "スキップ投票の有効・無効の設定をします。何も指定しないと現在の設定を確認できます。",
"args": {
"enabled": {
"description": "スキップ投票を有効にするかどうか。"
}
},
"currentState": "スキップ投票は現在 **{{status}}** の設定になっています。",
"changed": "スキップ投票を **{{status}}** にしました。"
},
"setting>nowplaying": {
"name": "設定>現在再生中",
"description": "現在再生中パネルの表示モードの設定をします。何も指定しないと現在の設定を確認できます。",
"args": {
"level": {
"description": "表示モードを指定します。",
"choices": {
"normal": "通常",
"silent": "サイレント",
"disabled": "無効"
}
}
},
"invalidLevel": "指定されたモードが正しくありません。normal, silent, disabledのいずれかを指定してください。",
"changed": "現在再生中パネルの表示モードを **{{level}}** に設定しました。",
"currentState": "現在再生中パネルの表示モードは現在 **{{level}}** に設定されています。"
},
"shuffle": {
"name": "シャッフル",
"description": "キューの内容をシャッフルします。",
Expand Down
4 changes: 3 additions & 1 deletion locales/ja/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,7 @@
"attachmentNotFound": "添付ファイルが見つかりません",
"invalidUrl": "有効なURLを指定してください。キーワードで再生する場合は`検索`コマンドを使用してください。"
},
"system": "システム"
"system": "システム",
"enabled": "有効",
"disabled": "無効"
}
9 changes: 4 additions & 5 deletions src/AudioSource/audiosource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import type { YouTube } from "./youtube";
import type { LoggerObject } from "../logger";
import type { i18n } from "i18next";
import type { EmbedField } from "oceanic.js";
import type { Readable } from "stream";

Expand Down Expand Up @@ -140,13 +139,13 @@ export abstract class AudioSource<T extends ThumbnailType, U extends AudioSource
}

/** 現在再生中の曲を示すEmbedFieldを生成します。 */
abstract toField(verbose: boolean, t: i18n["t"]): EmbedField[];
abstract toField(verbose: boolean): EmbedField[];
/** クラスを非同期で初期化します。 */
abstract init(url: string, prefetched: U | null, t: i18n["t"]): Promise<AudioSource<T, U>>;
abstract init(url: string, prefetched: U | null): Promise<AudioSource<T, U>>;
/** 再生するためのストリームをフェッチします。 */
abstract fetch(url?: boolean, t?: i18n["t"]): Promise<StreamInfo>;
abstract fetch(url?: boolean): Promise<StreamInfo>;
/** 現在再生中の曲に関する追加データを生成します。 */
abstract npAdditional(t: i18n["t"]): string;
abstract npAdditional(): string;
/** データをプレーンなオブジェクトにエクスポートします。 */
abstract exportData(): U;

Expand Down
11 changes: 8 additions & 3 deletions src/AudioSource/bestdori.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
import type { AudioSourceBasicJsonFormat, UrlStreamInfo } from ".";

import candyget from "candyget";
import { i18n } from "i18next";

import { AudioSource } from "./audiosource";
import { getCommandExecutionContext } from "../Commands";

export class BestdoriS extends AudioSource<string, BestdoriJsonFormat> {
protected artist = "";
Expand All @@ -31,7 +31,9 @@ export class BestdoriS extends AudioSource<string, BestdoriJsonFormat> {
protected arranger: string;
private id: number;

async init(url: string, prefetched: BestdoriJsonFormat, t: i18n["t"]){
async init(url: string, prefetched: BestdoriJsonFormat){
const { t } = getCommandExecutionContext();

this.url = url;
const id = BestdoriApi.instance.getAudioId(url);
if(!id) throw new Error("Invalid streamable url");
Expand Down Expand Up @@ -68,11 +70,14 @@ export class BestdoriS extends AudioSource<string, BestdoriJsonFormat> {
};
}

toField(_: boolean, t: i18n["t"]){
toField(_: boolean){
const { t } = getCommandExecutionContext();

const typeMap = {
anime: "カバー",
normal: "アニメ",
};

return [
{
name: "バンド名",
Expand Down
10 changes: 7 additions & 3 deletions src/AudioSource/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@
*/

import type { AudioSourceBasicJsonFormat, StreamInfo } from ".";
import type { i18n } from "i18next";

import { AudioSource } from "./audiosource";
import { getCommandExecutionContext } from "../Commands";
import { createFragmentalDownloadStream, downloadAsReadable, isAvailableRawAudioURL, requestHead, retrieveRemoteAudioInfo } from "../Util";

export class CustomStream extends AudioSource<string, AudioSourceBasicJsonFormat> {
constructor(){
super({ isCacheable: false });
}

async init(url: string, prefetched: AudioSourceBasicJsonFormat | null, t: i18n["t"]){
async init(url: string, prefetched: AudioSourceBasicJsonFormat | null){
const { t } = getCommandExecutionContext();

if(prefetched){
this.title = prefetched.title || t("audioSources.customStream");
this.url = url;
Expand Down Expand Up @@ -69,7 +71,9 @@ export class CustomStream extends AudioSource<string, AudioSourceBasicJsonFormat
};
}

toField(_: boolean, t: i18n["t"]){
toField(_: boolean){
const { t } = getCommandExecutionContext();

return [
{
name: ":link:URL",
Expand Down
10 changes: 7 additions & 3 deletions src/AudioSource/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,22 @@
*/

import type { AudioSourceBasicJsonFormat, ReadableStreamInfo } from "./audiosource";
import type { i18n } from "i18next";

import * as fs from "fs";
import * as path from "path";

import { AudioSource } from "./audiosource";
import { getCommandExecutionContext } from "../Commands";
import { retrieveRemoteAudioInfo } from "../Util";

export class FsStream extends AudioSource<string, AudioSourceBasicJsonFormat> {
constructor(){
super({ isCacheable: false });
}

async init(url: string, _: AudioSourceBasicJsonFormat | null, t: i18n["t"]){
async init(url: string, _: AudioSourceBasicJsonFormat | null){
const { t } = getCommandExecutionContext();

this.url = url;
const info = await retrieveRemoteAudioInfo(url);
this.title = info.displayTitle || t("audioSources.customStream");
Expand All @@ -46,7 +48,9 @@ export class FsStream extends AudioSource<string, AudioSourceBasicJsonFormat> {
};
}

toField(_: boolean, t: i18n["t"]){
toField(_: boolean){
const { t } = getCommandExecutionContext();

return [
{
name: `:asterisk:${t("moreInfo")}`,
Expand Down
10 changes: 7 additions & 3 deletions src/AudioSource/googledrive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,22 @@
*/

import type { AudioSourceBasicJsonFormat, UrlStreamInfo } from ".";
import type { i18n } from "i18next";

import candyget from "candyget";
import * as htmlEntities from "html-entities";

import { AudioSource } from "./audiosource";
import { getCommandExecutionContext } from "../Commands";
import { retrieveHttpStatusCode, retrieveRemoteAudioInfo } from "../Util";

export class GoogleDrive extends AudioSource<string, AudioSourceBasicJsonFormat> {
constructor(){
super({ isCacheable: false });
}

async init(url: string, prefetched: AudioSourceBasicJsonFormat | null, t: i18n["t"]){
async init(url: string, prefetched: AudioSourceBasicJsonFormat | null){
const { t } = getCommandExecutionContext();

if(prefetched){
this.title = prefetched.title || t("audioSources.driveStream");
this.url = url;
Expand All @@ -56,7 +58,9 @@ export class GoogleDrive extends AudioSource<string, AudioSourceBasicJsonFormat>
};
}

toField(_: boolean, t: i18n["t"]){
toField(_: boolean){
const { t } = getCommandExecutionContext();

return [
{
name: `:asterisk:${t("moreInfo")}`,
Expand Down
14 changes: 10 additions & 4 deletions src/AudioSource/niconico.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
*/

import type { AudioSourceBasicJsonFormat, ReadableStreamInfo } from ".";
import type { i18n } from "i18next";
import type { Readable } from "stream";

import { convert as htmlToText } from "html-to-text";
import NiconicoDL, { isValidURL } from "niconico-dl.js";

import { AudioSource } from "./audiosource";
import { getCommandExecutionContext } from "../Commands";
import { createPassThrough } from "../Util";

export class NicoNicoS extends AudioSource<string, NiconicoJsonFormat> {
Expand All @@ -35,7 +35,9 @@ export class NicoNicoS extends AudioSource<string, NiconicoJsonFormat> {
super({ isSeekable: false });
}

async init(url: string, prefetched: NiconicoJsonFormat, t: i18n["t"]){
async init(url: string, prefetched: NiconicoJsonFormat){
const { t } = getCommandExecutionContext();

this.url = url;
this.nico = new NiconicoDL(url, /* quality */ "high");
if(prefetched){
Expand Down Expand Up @@ -75,7 +77,9 @@ export class NicoNicoS extends AudioSource<string, NiconicoJsonFormat> {
};
}

toField(verbose: boolean, t: i18n["t"]){
toField(verbose: boolean){
const { t } = getCommandExecutionContext();

return [
{
name: `:cinema:${t("audioSources.videoAuthor")}`,
Expand All @@ -97,7 +101,9 @@ export class NicoNicoS extends AudioSource<string, NiconicoJsonFormat> {
];
}

npAdditional(t: i18n["t"]){
npAdditional(){
const { t } = getCommandExecutionContext();

return `${t("audioSources.videoAuthor")}: ` + this.author;
}

Expand Down
19 changes: 9 additions & 10 deletions src/AudioSource/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@

import type { KnownAudioSourceIdentifer } from "../Component/queueManager";
import type { SourceCache } from "../Component/sourceCache";
import type { i18n } from "i18next";

import * as AudioSource from ".";
import { isAvailableRawAudioURL } from "../Util";
import { useConfig } from "../config";
import { getConfig } from "../config";
import { getLogger } from "../logger";

type AudioSourceBasicInfo = {
Expand All @@ -32,10 +31,10 @@ type AudioSourceBasicInfo = {
forceCache: boolean,
};

const { isDisabledSource } = useConfig();
const { isDisabledSource } = getConfig();
const logger = getLogger("Resolver");

export async function resolve(info: AudioSourceBasicInfo, cacheManager: SourceCache, preventSourceCache: boolean, t: i18n["t"]){
export async function resolve(info: AudioSourceBasicInfo, cacheManager: SourceCache, preventSourceCache: boolean){
let basicInfo: AudioSource.AudioSource<any, any> | null = null;

const type = info.type;
Expand Down Expand Up @@ -66,29 +65,29 @@ export async function resolve(info: AudioSourceBasicInfo, cacheManager: SourceCa
basicInfo = await AudioSource.initYouTube(url, gotData as AudioSource.YouTubeJsonFormat, cache);
}else if(!isDisabledSource("custom") && (type === "custom" || type === "unknown" && isAvailableRawAudioURL(url))){
// カスタムストリーム
basicInfo = await new AudioSource.CustomStream().init(url, info.knownData, t);
basicInfo = await new AudioSource.CustomStream().init(url, info.knownData);
}else if(!isDisabledSource("soundcloud") && (type === "soundcloud" || AudioSource.SoundCloudS.validateUrl(url))){
// soundcloud
basicInfo = await new AudioSource.SoundCloudS().init(url, gotData as AudioSource.SoundcloudJsonFormat, t);
basicInfo = await new AudioSource.SoundCloudS().init(url, gotData as AudioSource.SoundcloudJsonFormat);
}else if(!isDisabledSource("spotify") && (type === "spotify" || AudioSource.Spotify.validateTrackUrl(url)) && AudioSource.Spotify.available){
// spotify
basicInfo = await new AudioSource.Spotify().init(url, gotData as AudioSource.SpotifyJsonFormat);
}else if(type === "unknown"){
// google drive
if(!isDisabledSource("googledrive") && AudioSource.GoogleDrive.validateUrl(url)){
basicInfo = await new AudioSource.GoogleDrive().init(url, info.knownData, t);
basicInfo = await new AudioSource.GoogleDrive().init(url, info.knownData);
}else if(!isDisabledSource("streamable") && AudioSource.StreamableApi.getVideoId(url)){
// Streamable
basicInfo = await new AudioSource.Streamable().init(url, gotData as AudioSource.StreamableJsonFormat);
}else if(process.env.BD_ENABLE && AudioSource.BestdoriApi.instance.getAudioId(url)){
// Bestdori
basicInfo = await new AudioSource.BestdoriS().init(url, gotData as AudioSource.BestdoriJsonFormat, t);
basicInfo = await new AudioSource.BestdoriS().init(url, gotData as AudioSource.BestdoriJsonFormat);
}else if(!isDisabledSource("niconico") && AudioSource.NicoNicoS.validateUrl(url)){
// NicoNico
basicInfo = await new AudioSource.NicoNicoS().init(url, gotData as AudioSource.NiconicoJsonFormat, t);
basicInfo = await new AudioSource.NicoNicoS().init(url, gotData as AudioSource.NiconicoJsonFormat);
}else if(!isDisabledSource("twitter") && AudioSource.Twitter.validateUrl(url)){
// Twitter
basicInfo = await new AudioSource.Twitter().init(url, gotData as AudioSource.TwitterJsonFormat, t);
basicInfo = await new AudioSource.Twitter().init(url, gotData as AudioSource.TwitterJsonFormat);
}
}

Expand Down
Loading