Add prefix command and general code cleanup

This commit is contained in:
Choco
2023-03-08 12:13:13 +08:00
parent b63301fa24
commit 18a9cb3995
9 changed files with 66 additions and 50 deletions

View File

@@ -10,7 +10,6 @@ from discord import app_commands
from discord.ext import commands from discord.ext import commands
from function import ( from function import (
langs, langs,
lang_guilds,
update_settings, update_settings,
get_settings, get_settings,
get_lang, get_lang,
@@ -21,7 +20,6 @@ from function import (
) )
from views import DebugModal, HelpView from views import DebugModal, HelpView
class Admin(commands.Cog, name="settings"): class Admin(commands.Cog, name="settings"):
def __init__(self, bot) -> None: def __init__(self, bot) -> None:
self.bot = bot self.bot = bot
@@ -45,6 +43,14 @@ class Admin(commands.Cog, name="settings"):
message = await ctx.send(embed=embed, view=view) message = await ctx.send(embed=embed, view=view)
view.response = message view.response = message
@settings.command(name="prefix", aliases=get_aliases("prefix"))
@commands.has_permissions(manage_guild=True)
@commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild)
async def prefix(self, ctx: commands.Context, prefix: str):
"Change the default prefix for message commands."
update_settings(ctx.guild.id, {"prefix": prefix})
await ctx.send(get_lang(ctx.guild.id, "setPrefix").format(ctx.prefix, prefix))
@settings.command(name="language", aliases=get_aliases("language")) @settings.command(name="language", aliases=get_aliases("language"))
@commands.has_permissions(manage_guild=True) @commands.has_permissions(manage_guild=True)
@commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild) @commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild)
@@ -57,9 +63,8 @@ class Admin(commands.Cog, name="settings"):
player, settings = self.get_settings(ctx) player, settings = self.get_settings(ctx)
if player: if player:
player.lang = language player.lang = language
lang_guilds[ctx.guild.id] = language
update_settings(ctx.guild.id, {'lang': language})
update_settings(ctx.guild.id, {'lang': language})
await ctx.send(get_lang(ctx.guild.id, 'changedLanguage').format(language)) await ctx.send(get_lang(ctx.guild.id, 'changedLanguage').format(language))
@language.autocomplete('language') @language.autocomplete('language')
@@ -134,12 +139,12 @@ class Admin(commands.Cog, name="settings"):
"Show all the bot settings in your server." "Show all the bot settings in your server."
player, settings = self.get_settings(ctx) player, settings = self.get_settings(ctx)
embed = discord.Embed(color=embed_color) embed = discord.Embed(color=embed_color)
embed.set_author(name=get_lang(ctx.guild.id, 'settingsMenu').format( embed.set_author(name=get_lang(ctx.guild.id, 'settingsMenu').format(ctx.guild.name), icon_url=self.bot.user.display_avatar.url)
ctx.guild.name), icon_url=self.bot.user.display_avatar.url)
if ctx.guild.icon: if ctx.guild.icon:
embed.set_thumbnail(url=ctx.guild.icon.url) embed.set_thumbnail(url=ctx.guild.icon.url)
embed.add_field(name=get_lang(ctx.guild.id, 'settingsTitle'), value=get_lang(ctx.guild.id, 'settingsValue').format( embed.add_field(name=get_lang(ctx.guild.id, 'settingsTitle'), value=get_lang(ctx.guild.id, 'settingsValue').format(
settings.get('prefix', func.bot_prefix),
settings.get('lang', 'EN'), settings.get('lang', 'EN'),
settings.get('controller', True), settings.get('controller', True),
f"<@&{settings['dj']}>" if 'dj' in settings else '`None`', f"<@&{settings['dj']}>" if 'dj' in settings else '`None`',

View File

@@ -55,7 +55,7 @@ class Task(commands.Cog):
@tasks.loop(hours=12.0) @tasks.loop(hours=12.0)
async def cache_cleaner(self): async def cache_cleaner(self):
func.lang_guilds.clear() func.guild_settings.clear()
func.playlist_name.clear() func.playlist_name.clear()
errorFile = func.gen_report() errorFile = func.gen_report()

View File

@@ -48,7 +48,7 @@ emoji_source_raw = {} #Stores all source emoji for track
error_log = {} #Stores error that not a Voicelink Exception error_log = {} #Stores error that not a Voicelink Exception
bot_access_user = [] #Stores bot access user id bot_access_user = [] #Stores bot access user id
langs = {} #Stores all the languages in ./langs langs = {} #Stores all the languages in ./langs
lang_guilds = {} #Cache guild language guild_settings = {} #Cache guild language
local_langs = {} #Stores all the localization languages in ./local_langs local_langs = {} #Stores all the localization languages in ./local_langs
playlist_name = {} #Cache the user's playlist name playlist_name = {} #Cache the user's playlist name
bot_prefix = "?" #The default bot prefix bot_prefix = "?" #The default bot prefix
@@ -61,23 +61,29 @@ controller_settings = {} #Stores the layout of music controller
nodes = {} nodes = {}
#-------------- Vocard Functions -------------- #-------------- Vocard Functions --------------
def get_settings(guildid:int): def get_settings(guild_id:int):
settings = collection.find_one({"_id":guildid}) settings = guild_settings.get(guild_id, None)
if not settings: if not settings:
collection.insert_one({"_id":guildid}) settings = collection.find_one({"_id":guild_id})
return {} if not settings:
collection.insert_one({"_id":guild_id})
settings = {}
guild_settings[guild_id] = settings
return settings return settings
def update_settings(guildid:int, data, mode="Set"): def update_settings(guild_id:int, data: dict, mode="Set"):
settings = get_settings(guild_id)
if mode == "Set": if mode == "Set":
collection.update_one({"_id":guildid}, {"$set":data}) for key, value in data.items():
if settings.get(key) != value:
guild_settings[guild_id][key] = value
collection.update_one({"_id":guild_id}, {"$set":data})
elif mode == "Delete": elif mode == "Delete":
collection.update_one({"_id":guildid}, {"$unset":data}) for key, value in data.items():
elif mode == "Add": if settings.get(key) != value:
collection.update_one({"_id":guildid}, {"$push":data}) del guild_settings[guild_id][key]
elif mode == "Remove": collection.update_one({"_id":guild_id}, {"$unset":data})
collection.update_one({"_id":guildid}, {"$pull":data}) return
return
def langs_setup(): def langs_setup():
for language in os.listdir(os.path.join(root_dir, "langs")): for language in os.listdir(os.path.join(root_dir, "langs")):
@@ -111,12 +117,8 @@ def settings_setup():
aliases_settings = rawSettings.get("aliases", {}) aliases_settings = rawSettings.get("aliases", {})
controller_settings = rawSettings.get("controller", [["back", "resume", "skip", {"stop": "red"}, "add"], ["tracks"]]) controller_settings = rawSettings.get("controller", [["back", "resume", "skip", {"stop": "red"}, "add"], ["tracks"]])
def get_lang(guildid:int, key:str): def get_lang(guild_id:int, key:str):
lang = lang_guilds.get(guildid) lang = get_settings(guild_id).get("lang", "EN")
if not lang:
settings = get_settings(guildid)
lang = lang_guilds[guildid] = settings.get('lang', 'EN')
return langs.get(lang, langs["EN"])[key] return langs.get(lang, langs["EN"])[key]
async def requests_api(url: str): async def requests_api(url: str):
@@ -213,8 +215,7 @@ async def similar_track(player) -> bool:
return False return False
if tracks: if tracks:
for track in tracks: await player.add_track(tracks)
player.queue.put(track)
return True return True
return False return False
@@ -263,8 +264,8 @@ def emoji_source(emoji:str):
def gen_report() -> Optional[discord.File]: def gen_report() -> Optional[discord.File]:
if error_log: if error_log:
errorText = "" errorText = ""
for guildId, error in error_log.items(): for guild_id, error in error_log.items():
errorText += f"Guild ID: {guildId}\n" + "-" * 30 + "\n" errorText += f"Guild ID: {guild_id}\n" + "-" * 30 + "\n"
for index, (key, value) in enumerate(error.items() , start=1): for index, (key, value) in enumerate(error.items() , start=1):
errorText += f"Error No: {index}, Time: {datetime.fromtimestamp(key)}\n" + value + "-" * 30 + "\n\n" errorText += f"Error No: {index}, Time: {datetime.fromtimestamp(key)}\n" + value + "-" * 30 + "\n\n"

View File

@@ -12,6 +12,7 @@
"notVote": "此命令需要您的投票!輸入 `/vote` 獲取更多信息。", "notVote": "此命令需要您的投票!輸入 `/vote` 獲取更多信息。",
"languageNotFound": "未找到語言包,請選擇一個存在的語言包。", "languageNotFound": "未找到語言包,請選擇一個存在的語言包。",
"changedLanguage": "已成功更改為`{0}`語言包。", "changedLanguage": "已成功更改為`{0}`語言包。",
"setPrefix": "完畢!我在您的服務器中的前綴現在是 `{0}`。嘗試運行 `{1}ping` 進行測試。",
"setDJ": "將 dj 設置為 {0}。", "setDJ": "將 dj 設置為 {0}。",
"setqueue": "將隊列模式設置為 `{0}`。", "setqueue": "將隊列模式設置為 `{0}`。",
"247": "現在您有了 `{0}` 24/7 模式。", "247": "現在您有了 `{0}` 24/7 模式。",
@@ -21,7 +22,7 @@
"toggleDuplicateTrack": "現在您已經 `{0}` 以防止隊列中的重複曲目。", "toggleDuplicateTrack": "現在您已經 `{0}` 以防止隊列中的重複曲目。",
"settingsMenu": "服務器設置 | {0}", "settingsMenu": "服務器設置 | {0}",
"settingsTitle": "❤️ 基本信息:", "settingsTitle": "❤️ 基本信息:",
"settingsValue": "語言:`{0}`\n啟用音樂控制器`{1}`\nDJ角色{2}\n投票繞過`{3}`\n24/7`{4}`\n默認音量`{5}%`\n播放時間: `{6}`", "settingsValue": "前綴:`{0}`\n語言:`{1}`\n啟用音樂控制器`{2}`\nDJ角色{3}\n投票繞過`{4}`\n24/7`{5}`\n默認音量`{6}%`\n播放時間: `{7}`",
"settingsTitle2": "🔗 隊列信息:", "settingsTitle2": "🔗 隊列信息:",
"settingsValue2": "隊列模式: `{0}`\n最大曲目: `{1}`\n允許重複軌道: `{2}`", "settingsValue2": "隊列模式: `{0}`\n最大曲目: `{1}`\n允許重複軌道: `{2}`",
"settingsPermTitle": "✨ 權限:", "settingsPermTitle": "✨ 權限:",

View File

@@ -12,6 +12,7 @@
"notVote": "Dieser Befehl erfordert Ihre Stimme! Geben Sie `/vote` ein, um weitere Informationen zu erhalten.", "notVote": "Dieser Befehl erfordert Ihre Stimme! Geben Sie `/vote` ein, um weitere Informationen zu erhalten.",
"languageNotFound": "Kein Sprachpaket gefunden, bitte wählen Sie ein vorhandenes Sprachpaket aus.", "languageNotFound": "Kein Sprachpaket gefunden, bitte wählen Sie ein vorhandenes Sprachpaket aus.",
"changedLanguage": "Erfolgreich auf das Sprachpaket `{0}` geändert.", "changedLanguage": "Erfolgreich auf das Sprachpaket `{0}` geändert.",
"setPrefix": "Erledigt! Mein Präfix auf Ihrem Server ist jetzt `{0}`. Versuchen Sie, `{1}ping` auszuführen, um es zu testen.",
"setDJ": "Stellen Sie den DJ auf {0}.", "setDJ": "Stellen Sie den DJ auf {0}.",
"setqueue": "Stellen Sie den Warteschlangenmodus auf `{0}` ein.", "setqueue": "Stellen Sie den Warteschlangenmodus auf `{0}` ein.",
"247": "Jetzt haben Sie den 24/7-Modus von `{0}`.", "247": "Jetzt haben Sie den 24/7-Modus von `{0}`.",
@@ -21,7 +22,7 @@
"toggleDuplicateTrack": "Jetzt haben Sie `{0}`, um doppelte Tracks in der Warteschlange zu verhindern.", "toggleDuplicateTrack": "Jetzt haben Sie `{0}`, um doppelte Tracks in der Warteschlange zu verhindern.",
"settingsMenu": "Servereinstellungen | {0}", "settingsMenu": "Servereinstellungen | {0}",
"settingsTitle": "❤️ Grundlegende Informationen:", "settingsTitle": "❤️ Grundlegende Informationen:",
"settingsValue": "Sprache: `{0}`\nMusik-Controller aktivieren: `{1}`\nDJ-Rolle: {2}\nAbstimmungsumgehung: `{3}`\nRund um die Uhr: `{4}`\nStandardvolumen: `{5} %`\nSpielzeit: `{6}`", "settingsValue": "Präfix: `{0}`\nSprache: `{1}`\nMusik-Controller aktivieren: `{2}`\nDJ-Rolle: {3}\nAbstimmungsumgehung: `{4}`\nRund um die Uhr: `{5}`\nStandardvolumen: `{6} %`\nSpielzeit: `{7}`",
"settingsTitle2": "🔗 Warteschlangeninformationen:", "settingsTitle2": "🔗 Warteschlangeninformationen:",
"settingsValue2": "Warteschlangenmodus: `{0}`\nMax Lied: `{1}`\nDoppelte Spur zulassen: `{2}`", "settingsValue2": "Warteschlangenmodus: `{0}`\nMax Lied: `{1}`\nDoppelte Spur zulassen: `{2}`",
"settingsPermTitle": "✨ Berechtigungen:", "settingsPermTitle": "✨ Berechtigungen:",

View File

@@ -12,6 +12,7 @@
"notVote": "This command requires your vote! Type `/vote` for more info.", "notVote": "This command requires your vote! Type `/vote` for more info.",
"languageNotFound": "No language pack found! please select an existing language pack.", "languageNotFound": "No language pack found! please select an existing language pack.",
"changedLanguage": "Successfully changed to `{0}` language pack.", "changedLanguage": "Successfully changed to `{0}` language pack.",
"setPrefix": "Done! My prefix in your server is now `{0}`. Try running `{1}ping` to test it out.",
"setDJ": "Set the dj to {0}.", "setDJ": "Set the dj to {0}.",
"setqueue": "Set queue mode to `{0}`.", "setqueue": "Set queue mode to `{0}`.",
"247": "Now you have `{0}` 24/7 mode.", "247": "Now you have `{0}` 24/7 mode.",
@@ -21,7 +22,7 @@
"toggleDuplicateTrack": "Now you have `{0}` to prevent duplicate track in the queue.", "toggleDuplicateTrack": "Now you have `{0}` to prevent duplicate track in the queue.",
"settingsMenu": "Server Settings | {0}", "settingsMenu": "Server Settings | {0}",
"settingsTitle": "❤️ Basic Info:", "settingsTitle": "❤️ Basic Info:",
"settingsValue": "Language: `{0}`\nEnable Music Controller: `{1}`\nDJ Role: {2}\nVote Bypass: `{3}`\n24/7: `{4}`\nDefault Volume: `{5}%`\nPlay Time: `{6}`", "settingsValue": "Prefix: `{0}`\nLanguage: `{1}`\nEnable Music Controller: `{2}`\nDJ Role: {3}\nVote Bypass: `{4}`\n24/7: `{5}`\nDefault Volume: `{6}%`\nPlay Time: `{7}`",
"settingsTitle2": "🔗 Queue info:", "settingsTitle2": "🔗 Queue info:",
"settingsValue2": "Queue Mode: `{0}`\nMax Song: `{1}`\nAllow Duplicate Track: `{2}`", "settingsValue2": "Queue Mode: `{0}`\nMax Song: `{1}`\nAllow Duplicate Track: `{2}`",
"settingsPermTitle": "✨ Permissions:", "settingsPermTitle": "✨ Permissions:",

View File

@@ -12,6 +12,7 @@
"notVote": "このコマンドにはあなたの投票が必要です! 詳細については、`/vote` と入力してください。", "notVote": "このコマンドにはあなたの投票が必要です! 詳細については、`/vote` と入力してください。",
"languageNotFound": "言語パックが見つかりません。既存の言語パックを選択してください", "languageNotFound": "言語パックが見つかりません。既存の言語パックを選択してください",
"changedLanguage": "`{0}` 言語パックに正常に変更されました。", "changedLanguage": "`{0}` 言語パックに正常に変更されました。",
"setPrefix": "終わり! あなたのサーバーの私のプレフィックスは現在`{0}`です。 `{1}ping` を実行してテストしてみてください。",
"setDJ": "DJ を {0} に設定します。", "setDJ": "DJ を {0} に設定します。",
"setqueue": "キュー モードを `{0}` に設定します。", "setqueue": "キュー モードを `{0}` に設定します。",
"247": "これで `{0}` 24/7 モードになりました。", "247": "これで `{0}` 24/7 モードになりました。",
@@ -21,7 +22,7 @@
"toggleDuplicateTrack": "これで、キュー内でトラックが重複しないように `{0}` になりました。", "toggleDuplicateTrack": "これで、キュー内でトラックが重複しないように `{0}` になりました。",
"settingsMenu": "サーバー設定 | {0}", "settingsMenu": "サーバー設定 | {0}",
"settingsTitle": "❤️ 基本情報:", "settingsTitle": "❤️ 基本情報:",
"settingsValue": "言語: `{0}`\nミュージック コントローラを有効にする: `{1}`\nDJ ロール: {2}\n投票バイパス: `{3}`\n年中無休: `{4}`\nデフォルトのボリューム: `{5}%`\nプレイ時間: `{6}`", "settingsValue": "プレフィックス: `{0}`\n言語: `{1}`\nミュージック コントローラを有効にする: `{2}`\nDJ ロール: {3}\n投票バイパス: `{4}`\n年中無休: `{5}`\nデフォルトのボリューム: `{6}%`\nプレイ時間: `{7}`",
"settingsTitle2": "🔗 キュー情報:", "settingsTitle2": "🔗 キュー情報:",
"settingsValue2": "キューモード: `{0}`\nマックスソング: `{1}`\n重複トラックを許可: `{2}`", "settingsValue2": "キューモード: `{0}`\nマックスソング: `{1}`\n重複トラックを許可: `{2}`",
"settingsPermTitle": "✨ パーミッション:", "settingsPermTitle": "✨ パーミッション:",

View File

@@ -12,6 +12,7 @@
"notVote": "이 명령에는 투표가 필요합니다! 자세한 정보를 보려면 `/vote`를 입력하세요.", "notVote": "이 명령에는 투표가 필요합니다! 자세한 정보를 보려면 `/vote`를 입력하세요.",
"languageNotFound": "언어 팩을 찾을 수 없습니다. 기존 언어 팩을 선택하십시오.", "languageNotFound": "언어 팩을 찾을 수 없습니다. 기존 언어 팩을 선택하십시오.",
"changedLanguage": "`{0}` 언어 팩으로 변경되었습니다.", "changedLanguage": "`{0}` 언어 팩으로 변경되었습니다.",
"setPrefix": "완료! 서버의 내 접두어는 이제 `{0}`입니다. `{1}ping`을 실행하여 테스트해 보십시오.",
"setDJ": "DJ를 {0}으로 설정합니다.", "setDJ": "DJ를 {0}으로 설정합니다.",
"setqueue": "대기열 모드를 `{0}`로 설정합니다.", "setqueue": "대기열 모드를 `{0}`로 설정합니다.",
"247": "이제 `{0}` 24/7 모드가 있습니다.", "247": "이제 `{0}` 24/7 모드가 있습니다.",
@@ -21,7 +22,7 @@
"toggleDuplicateTrack": "이제 대기열에서 중복 트랙을 방지하기 위해 `{0}`되었습니다.", "toggleDuplicateTrack": "이제 대기열에서 중복 트랙을 방지하기 위해 `{0}`되었습니다.",
"settingsMenu": "서버 설정 | {0}", "settingsMenu": "서버 설정 | {0}",
"settingsTitle": "❤️ 기본 정보:", "settingsTitle": "❤️ 기본 정보:",
"settingsValue": "어: `{0}`\n음악 컨트롤러 활성화: `{1}`\nDJ 역할: {2}\n투표 우회: `{3}`\n연중무휴: `{4}`\n기본 볼륨: `{5}%`\n플레이 시간: `{6}`", "settingsValue": "접두어: `{0}`\n언어: `{1}`\n음악 컨트롤러 활성화: `{2}`\nDJ 역할: {3}\n투표 우회: `{4}`\n연중무휴: `{5}`\n기본 볼륨: `{6}%`\n플레이 시간: `{7}`",
"settingsTitle2": "🔗 대기열 정보:", "settingsTitle2": "🔗 대기열 정보:",
"settingsValue2": "대기열 모드: `{0}`\n맥스송: `{1}`\n중복 트랙 허용: `{2}`", "settingsValue2": "대기열 모드: `{0}`\n맥스송: `{1}`\n중복 트랙 허용: `{2}`",
"settingsPermTitle": "✨ 권한:", "settingsPermTitle": "✨ 권한:",

35
main.py
View File

@@ -1,7 +1,7 @@
import discord import discord
import sys, os, traceback, aiohttp import sys, os, traceback, aiohttp
import update import update
import function import function as func
from discord.ext import commands from discord.ext import commands
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -9,7 +9,7 @@ from datetime import datetime
from voicelink import VoicelinkException from voicelink import VoicelinkException
load_dotenv() load_dotenv()
function.settings_setup() func.settings_setup()
class Translator(discord.app_commands.Translator): class Translator(discord.app_commands.Translator):
async def load(self): async def load(self):
@@ -19,8 +19,8 @@ class Translator(discord.app_commands.Translator):
print("Unload Translator") print("Unload Translator")
async def translate(self, string: discord.app_commands.locale_str, locale: discord.Locale, context: discord.app_commands.TranslationContext): async def translate(self, string: discord.app_commands.locale_str, locale: discord.Locale, context: discord.app_commands.TranslationContext):
if str(locale) in function.local_langs: if str(locale) in func.local_langs:
return function.local_langs[str(locale)].get(string.message, None) return func.local_langs[str(locale)].get(string.message, None)
return None return None
class Vocard(commands.Bot): class Vocard(commands.Bot):
@@ -30,13 +30,13 @@ class Vocard(commands.Bot):
return False return False
if self.user.mentioned_in(message) and not message.mention_everyone: if self.user.mentioned_in(message) and not message.mention_everyone:
await message.channel.send(f"My prefix is `{self.command_prefix}`") await message.channel.send(f"My prefix is `{await self.command_prefix(self, message)}`")
await self.process_commands(message) await self.process_commands(message)
async def setup_hook(self): async def setup_hook(self):
function.langs_setup() func.langs_setup()
for module in os.listdir(function.root_dir + '/cogs'): for module in os.listdir(func.root_dir + '/cogs'):
if module.endswith('.py'): if module.endswith('.py'):
try: try:
await self.load_extension(f"cogs.{module[:-3]}") await self.load_extension(f"cogs.{module[:-3]}")
@@ -89,21 +89,25 @@ class CommandCheck(discord.app_commands.CommandTree):
return await super().interaction_check(interaction) return await super().interaction_check(interaction)
async def get_prefix(bot, message: discord.Message):
settings = func.get_settings(message.guild.id)
return settings.get("prefix", func.bot_prefix)
intents = discord.Intents.default() intents = discord.Intents.default()
intents.members = True intents.members = True
intents.message_content = True if function.bot_prefix else False intents.message_content = True if func.bot_prefix else False
member_cache = discord.MemberCacheFlags( member_cache = discord.MemberCacheFlags(
voice=True, voice=True,
joined=False joined=False
) )
bot = Vocard(command_prefix=function.bot_prefix, bot = Vocard(command_prefix=get_prefix,
help_command=None, help_command=None,
tree_cls=CommandCheck, tree_cls=CommandCheck,
chunk_guilds_at_startup = False, chunk_guilds_at_startup=False,
member_cache_flags=member_cache, member_cache_flags=member_cache,
activity=discord.Activity(type=discord.ActivityType.listening,name="/help"), activity=discord.Activity(type=discord.ActivityType.listening, name="/help"),
case_insensitive = True, case_insensitive=True,
intents=intents) intents=intents)
@bot.tree.error @bot.tree.error
@@ -126,5 +130,6 @@ async def app_command_error(interaction: discord.Interaction, error):
except: except:
pass pass
update.checkVersion(withMsg = True) if __name__ == "__main__":
bot.run(os.getenv("TOKEN"), log_handler=None) update.checkVersion(withMsg=True)
bot.run(os.getenv("TOKEN"))