mirror of
https://github.com/BluemediaGER/muse.git
synced 2024-11-23 09:15:29 +01:00
Reorg third party services & config
This commit is contained in:
parent
79e7e88fab
commit
efcdeb78c8
|
@ -10,6 +10,7 @@ import handleGuildCreate from './events/guild-create';
|
||||||
import handleVoiceStateUpdate from './events/voice-state-update';
|
import handleVoiceStateUpdate from './events/voice-state-update';
|
||||||
import errorMsg from './utils/error-msg';
|
import errorMsg from './utils/error-msg';
|
||||||
import {isUserInVoice} from './utils/channels';
|
import {isUserInVoice} from './utils/channels';
|
||||||
|
import Config from './services/config';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export default class {
|
export default class {
|
||||||
|
@ -18,10 +19,10 @@ export default class {
|
||||||
private readonly token: string;
|
private readonly token: string;
|
||||||
private readonly commands!: Collection<string, Command>;
|
private readonly commands!: Collection<string, Command>;
|
||||||
|
|
||||||
constructor(@inject(TYPES.Client) client: Client, @inject(TYPES.Services.NaturalLanguage) naturalLanguage: NaturalLanguage, @inject(TYPES.Config.DISCORD_TOKEN) token: string) {
|
constructor(@inject(TYPES.Client) client: Client, @inject(TYPES.Services.NaturalLanguage) naturalLanguage: NaturalLanguage, @inject(TYPES.Config) config: Config) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.naturalLanguage = naturalLanguage;
|
this.naturalLanguage = naturalLanguage;
|
||||||
this.token = token;
|
this.token = config.DISCORD_TOKEN;
|
||||||
this.commands = new Collection();
|
this.commands = new Collection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
25
src/index.ts
25
src/index.ts
|
@ -1,31 +1,20 @@
|
||||||
import Spotify from 'spotify-web-api-node';
|
|
||||||
import makeDir from 'make-dir';
|
import makeDir from 'make-dir';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import container from './inversify.config';
|
import container from './inversify.config';
|
||||||
import {TYPES} from './types';
|
import {TYPES} from './types';
|
||||||
import Bot from './bot';
|
import Bot from './bot';
|
||||||
import {sequelize} from './utils/db';
|
import {sequelize} from './utils/db';
|
||||||
|
import Config from './services/config';
|
||||||
|
|
||||||
let bot = container.get<Bot>(TYPES.Bot);
|
const bot = container.get<Bot>(TYPES.Bot);
|
||||||
const spotify = container.get<Spotify>(TYPES.Lib.Spotify);
|
|
||||||
|
|
||||||
const refreshSpotifyToken = async () => {
|
|
||||||
const auth = await spotify.clientCredentialsGrant();
|
|
||||||
|
|
||||||
spotify.setAccessToken(auth.body.access_token);
|
|
||||||
|
|
||||||
return auth.body.expires_in;
|
|
||||||
};
|
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const spotifyRefreshIntervalSeconds = await refreshSpotifyToken();
|
|
||||||
|
|
||||||
setInterval(async () => refreshSpotifyToken(), (spotifyRefreshIntervalSeconds / 2) * 1000);
|
|
||||||
|
|
||||||
// Create data directories if necessary
|
// Create data directories if necessary
|
||||||
await makeDir(container.get(TYPES.Config.DATA_DIR));
|
const config = container.get<Config>(TYPES.Config);
|
||||||
await makeDir(container.get(TYPES.Config.CACHE_DIR));
|
|
||||||
await makeDir(path.join(container.get(TYPES.Config.CACHE_DIR), 'tmp'));
|
await makeDir(config.DATA_DIR);
|
||||||
|
await makeDir(config.CACHE_DIR);
|
||||||
|
await makeDir(path.join(config.CACHE_DIR, 'tmp'));
|
||||||
|
|
||||||
await sequelize.sync({});
|
await sequelize.sync({});
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,7 @@ import {Container} from 'inversify';
|
||||||
import {TYPES} from './types';
|
import {TYPES} from './types';
|
||||||
import Bot from './bot';
|
import Bot from './bot';
|
||||||
import {Client} from 'discord.js';
|
import {Client} from 'discord.js';
|
||||||
import YouTube from 'youtube.ts';
|
import ConfigProvider from './services/config';
|
||||||
import Spotify from 'spotify-web-api-node';
|
|
||||||
import {
|
|
||||||
DISCORD_TOKEN,
|
|
||||||
YOUTUBE_API_KEY,
|
|
||||||
SPOTIFY_CLIENT_ID,
|
|
||||||
SPOTIFY_CLIENT_SECRET,
|
|
||||||
DATA_DIR,
|
|
||||||
CACHE_DIR
|
|
||||||
} from './utils/config';
|
|
||||||
|
|
||||||
// Managers
|
// Managers
|
||||||
import PlayerManager from './managers/player';
|
import PlayerManager from './managers/player';
|
||||||
|
@ -36,6 +27,7 @@ import Shortcuts from './commands/shortcuts';
|
||||||
import Shuffle from './commands/shuffle';
|
import Shuffle from './commands/shuffle';
|
||||||
import Skip from './commands/skip';
|
import Skip from './commands/skip';
|
||||||
import Unskip from './commands/unskip';
|
import Unskip from './commands/unskip';
|
||||||
|
import ThirdParty from './services/third-party';
|
||||||
|
|
||||||
let container = new Container();
|
let container = new Container();
|
||||||
|
|
||||||
|
@ -70,13 +62,9 @@ container.bind<NaturalLanguage>(TYPES.Services.NaturalLanguage).to(NaturalLangua
|
||||||
});
|
});
|
||||||
|
|
||||||
// Config values
|
// Config values
|
||||||
container.bind<string>(TYPES.Config.DISCORD_TOKEN).toConstantValue(DISCORD_TOKEN);
|
container.bind(TYPES.Config).toConstantValue(new ConfigProvider());
|
||||||
container.bind<string>(TYPES.Config.YOUTUBE_API_KEY).toConstantValue(YOUTUBE_API_KEY);
|
|
||||||
container.bind<string>(TYPES.Config.DATA_DIR).toConstantValue(DATA_DIR);
|
|
||||||
container.bind<string>(TYPES.Config.CACHE_DIR).toConstantValue(CACHE_DIR);
|
|
||||||
|
|
||||||
// Static libraries
|
// Static libraries
|
||||||
container.bind<YouTube>(TYPES.Lib.YouTube).toConstantValue(new YouTube(YOUTUBE_API_KEY));
|
container.bind(TYPES.ThirdParty).to(ThirdParty);
|
||||||
container.bind<Spotify>(TYPES.Lib.Spotify).toConstantValue(new Spotify({clientId: SPOTIFY_CLIENT_ID, clientSecret: SPOTIFY_CLIENT_SECRET}));
|
|
||||||
|
|
||||||
export default container;
|
export default container;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {inject, injectable} from 'inversify';
|
||||||
import {TYPES} from '../types';
|
import {TYPES} from '../types';
|
||||||
import Player from '../services/player';
|
import Player from '../services/player';
|
||||||
import {Client} from 'discord.js';
|
import {Client} from 'discord.js';
|
||||||
|
import Config from '../services/config';
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export default class {
|
export default class {
|
||||||
|
@ -9,9 +10,9 @@ export default class {
|
||||||
private readonly cacheDir: string;
|
private readonly cacheDir: string;
|
||||||
private readonly discordClient: Client;
|
private readonly discordClient: Client;
|
||||||
|
|
||||||
constructor(@inject(TYPES.Config.CACHE_DIR) cacheDir: string, @inject(TYPES.Client) client: Client) {
|
constructor(@inject(TYPES.Config) config: Config, @inject(TYPES.Client) client: Client) {
|
||||||
this.guildPlayers = new Map();
|
this.guildPlayers = new Map();
|
||||||
this.cacheDir = cacheDir;
|
this.cacheDir = config.CACHE_DIR;
|
||||||
this.discordClient = client;
|
this.discordClient = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
36
src/services/config.ts
Normal file
36
src/services/config.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import dotenv from 'dotenv';
|
||||||
|
import {injectable} from 'inversify';
|
||||||
|
import path from 'path';
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
export const DATA_DIR = path.resolve(process.env.DATA_DIR ? process.env.DATA_DIR : './data');
|
||||||
|
|
||||||
|
const CONFIG_MAP = {
|
||||||
|
DISCORD_TOKEN: process.env.DISCORD_TOKEN,
|
||||||
|
YOUTUBE_API_KEY: process.env.YOUTUBE_API_KEY,
|
||||||
|
SPOTIFY_CLIENT_ID: process.env.SPOTIFY_CLIENT_ID,
|
||||||
|
SPOTIFY_CLIENT_SECRET: process.env.SPOTIFY_CLIENT_SECRET,
|
||||||
|
DATA_DIR,
|
||||||
|
CACHE_DIR: path.join(DATA_DIR, 'cache')
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export default class Config {
|
||||||
|
readonly DISCORD_TOKEN!: string;
|
||||||
|
readonly YOUTUBE_API_KEY!: string;
|
||||||
|
readonly SPOTIFY_CLIENT_ID!: string;
|
||||||
|
readonly SPOTIFY_CLIENT_SECRET!: string;
|
||||||
|
readonly DATA_DIR!: string;
|
||||||
|
readonly CACHE_DIR!: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
for (const [key, value] of Object.entries(CONFIG_MAP)) {
|
||||||
|
if (typeof value === 'undefined') {
|
||||||
|
console.error(`Missing environment variable for ${key}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this[key as keyof typeof CONFIG_MAP] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,8 @@ import {Except} from 'type-fest';
|
||||||
import {QueuedSong, QueuedPlaylist} from '../services/player';
|
import {QueuedSong, QueuedPlaylist} from '../services/player';
|
||||||
import {TYPES} from '../types';
|
import {TYPES} from '../types';
|
||||||
import {cleanUrl} from '../utils/url';
|
import {cleanUrl} from '../utils/url';
|
||||||
|
import ThirdParty from './third-party';
|
||||||
|
import Config from './config';
|
||||||
|
|
||||||
type QueuedSongWithoutChannel = Except<QueuedSong, 'addedInChannelId'>;
|
type QueuedSongWithoutChannel = Except<QueuedSong, 'addedInChannelId'>;
|
||||||
|
|
||||||
|
@ -20,10 +22,10 @@ export default class {
|
||||||
private readonly youtubeKey: string;
|
private readonly youtubeKey: string;
|
||||||
private readonly spotify: Spotify;
|
private readonly spotify: Spotify;
|
||||||
|
|
||||||
constructor(@inject(TYPES.Lib.YouTube) youtube: YouTube, @inject(TYPES.Config.YOUTUBE_API_KEY) youtubeKey: string, @inject(TYPES.Lib.Spotify) spotify: Spotify) {
|
constructor(@inject(TYPES.ThirdParty) thirdParty: ThirdParty, @inject(TYPES.Config) config: Config) {
|
||||||
this.youtube = youtube;
|
this.youtube = thirdParty.youtube;
|
||||||
this.youtubeKey = youtubeKey;
|
this.youtubeKey = config.YOUTUBE_API_KEY;
|
||||||
this.spotify = spotify;
|
this.spotify = thirdParty.spotify;
|
||||||
}
|
}
|
||||||
|
|
||||||
async youtubeVideoSearch(query: string): Promise<QueuedSongWithoutChannel|null> {
|
async youtubeVideoSearch(query: string): Promise<QueuedSongWithoutChannel|null> {
|
||||||
|
@ -214,7 +216,7 @@ export default class {
|
||||||
private async spotifyToYouTube(track: SpotifyApi.TrackObjectSimplified, _: QueuedPlaylist | null): Promise<QueuedSongWithoutChannel | null> {
|
private async spotifyToYouTube(track: SpotifyApi.TrackObjectSimplified, _: QueuedPlaylist | null): Promise<QueuedSongWithoutChannel | null> {
|
||||||
try {
|
try {
|
||||||
const {items} = await this.youtube.videos.search({q: `"${track.name}" "${track.artists[0].name}"`, maxResults: 10});
|
const {items} = await this.youtube.videos.search({q: `"${track.name}" "${track.artists[0].name}"`, maxResults: 10});
|
||||||
const videoResult = items[0]; // Items.find(item => item.type === 'video');
|
const videoResult = items[0];
|
||||||
|
|
||||||
if (!videoResult) {
|
if (!videoResult) {
|
||||||
throw new Error('No video found for query.');
|
throw new Error('No video found for query.');
|
||||||
|
|
36
src/services/third-party.ts
Normal file
36
src/services/third-party.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import {inject, injectable} from 'inversify';
|
||||||
|
import SpotifyWebApi from 'spotify-web-api-node';
|
||||||
|
import Youtube from 'youtube.ts';
|
||||||
|
import {TYPES} from '../types';
|
||||||
|
import Config from './config';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export default class ThirdParty {
|
||||||
|
readonly youtube: Youtube;
|
||||||
|
readonly spotify: SpotifyWebApi;
|
||||||
|
|
||||||
|
private spotifyTokenTimerId?: NodeJS.Timeout;
|
||||||
|
|
||||||
|
constructor(@inject(TYPES.Config) config: Config) {
|
||||||
|
this.youtube = new Youtube(config.YOUTUBE_API_KEY);
|
||||||
|
this.spotify = new SpotifyWebApi({
|
||||||
|
clientId: config.SPOTIFY_CLIENT_ID,
|
||||||
|
clientSecret: config.SPOTIFY_CLIENT_SECRET
|
||||||
|
});
|
||||||
|
|
||||||
|
void this.refreshSpotifyToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if (this.spotifyTokenTimerId) {
|
||||||
|
clearTimeout(this.spotifyTokenTimerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async refreshSpotifyToken() {
|
||||||
|
const auth = await this.spotify.clientCredentialsGrant();
|
||||||
|
this.spotify.setAccessToken(auth.body.access_token);
|
||||||
|
|
||||||
|
this.spotifyTokenTimerId = setTimeout(this.refreshSpotifyToken, (auth.body.expires_in / 2) * 1000);
|
||||||
|
}
|
||||||
|
}
|
12
src/types.ts
12
src/types.ts
|
@ -1,17 +1,9 @@
|
||||||
export const TYPES = {
|
export const TYPES = {
|
||||||
Bot: Symbol('Bot'),
|
Bot: Symbol('Bot'),
|
||||||
Client: Symbol('Client'),
|
Client: Symbol('Client'),
|
||||||
Config: {
|
Config: Symbol('Config'),
|
||||||
DISCORD_TOKEN: Symbol('DISCORD_TOKEN'),
|
|
||||||
YOUTUBE_API_KEY: Symbol('YOUTUBE_API_KEY'),
|
|
||||||
DATA_DIR: Symbol('DATA_DIR'),
|
|
||||||
CACHE_DIR: Symbol('CACHE_DIR')
|
|
||||||
},
|
|
||||||
Command: Symbol('Command'),
|
Command: Symbol('Command'),
|
||||||
Lib: {
|
ThirdParty: Symbol('ThirdParty'),
|
||||||
YouTube: Symbol('YouTube'),
|
|
||||||
Spotify: Symbol('Spotify')
|
|
||||||
},
|
|
||||||
Managers: {
|
Managers: {
|
||||||
Player: Symbol('PlayerManager')
|
Player: Symbol('PlayerManager')
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
import dotenv from 'dotenv';
|
|
||||||
import path from 'path';
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
export const DISCORD_TOKEN: string = process.env.DISCORD_TOKEN ? process.env.DISCORD_TOKEN : '';
|
|
||||||
export const YOUTUBE_API_KEY: string = process.env.YOUTUBE_API_KEY ? process.env.YOUTUBE_API_KEY : '';
|
|
||||||
export const SPOTIFY_CLIENT_ID: string = process.env.SPOTIFY_CLIENT_ID ? process.env.SPOTIFY_CLIENT_ID : '';
|
|
||||||
export const SPOTIFY_CLIENT_SECRET: string = process.env.SPOTIFY_CLIENT_SECRET ? process.env.SPOTIFY_CLIENT_SECRET : '';
|
|
||||||
export const DATA_DIR = path.resolve(process.env.DATA_DIR ? process.env.DATA_DIR : './data');
|
|
||||||
export const CACHE_DIR = path.join(DATA_DIR, 'cache');
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {Sequelize} from 'sequelize-typescript';
|
import {Sequelize} from 'sequelize-typescript';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {DATA_DIR} from '../utils/config';
|
import {DATA_DIR} from '../services/config';
|
||||||
import {Settings, Shortcut} from '../models';
|
import {Settings, Shortcut} from '../models';
|
||||||
|
|
||||||
export const sequelize = new Sequelize({
|
export const sequelize = new Sequelize({
|
||||||
|
|
Loading…
Reference in a new issue