add(i18n): create a language switcher command for all guilds

This commit is contained in:
Romain J 2019-09-29 23:01:49 +02:00
parent 29808d41d6
commit d5f1f71a0a
8 changed files with 125 additions and 71 deletions

View file

@ -9,6 +9,7 @@ import humanize
from discord.ext import commands from discord.ext import commands
from bot import TuxBot from bot import TuxBot
from .utils.models.lang import Lang
from .utils.lang import Texts from .utils.lang import Texts
from .utils.models.warn import Warn from .utils.models.warn import Warn
@ -35,7 +36,7 @@ class Admin(commands.Cog):
member: discord.Member = kwargs.get('member') member: discord.Member = kwargs.get('member')
reason = kwargs.get( reason = kwargs.get(
'reason', 'reason',
Texts('admin').get("Please enter a reason") Texts('admin', ctx).get("Please enter a reason")
) )
if kwargs.get('type') == 'ban': if kwargs.get('type') == 'ban':
@ -86,7 +87,7 @@ class Admin(commands.Cog):
message_id) message_id)
await message.edit(content=content) await message.edit(content=content)
except (discord.errors.NotFound, discord.errors.Forbidden): except (discord.errors.NotFound, discord.errors.Forbidden):
await ctx.send(Texts('utils').get("Unable to find the message"), await ctx.send(Texts('utils', ctx).get("Unable to find the message"),
delete_after=5) delete_after=5)
@_say.command(name='to') @_say.command(name='to')
@ -119,10 +120,10 @@ class Admin(commands.Cog):
await ctx.send(embed=e) await ctx.send(embed=e)
except discord.Forbidden: except discord.Forbidden:
await ctx.send(Texts('admin').get("Unable to ban this user"), await ctx.send(Texts('admin', ctx).get("Unable to ban this user"),
delete_after=5) delete_after=5)
except discord.errors.NotFound: except discord.errors.NotFound:
await ctx.send(Texts('utils').get("Unable to find the user..."), await ctx.send(Texts('utils', ctx).get("Unable to find the user..."),
delete_after=5) delete_after=5)
"""---------------------------------------------------------------------""" """---------------------------------------------------------------------"""
@ -144,10 +145,10 @@ class Admin(commands.Cog):
await ctx.send(embed=e) await ctx.send(embed=e)
except discord.Forbidden: except discord.Forbidden:
await ctx.send(Texts('admin').get("Unable to kick this user"), await ctx.send(Texts('admin', ctx).get("Unable to kick this user"),
delete_after=5) delete_after=5)
except discord.errors.NotFound: except discord.errors.NotFound:
await ctx.send(Texts('utils').get("Unable to find the user..."), await ctx.send(Texts('utils', ctx).get("Unable to find the user..."),
delete_after=5) delete_after=5)
"""---------------------------------------------------------------------""" """---------------------------------------------------------------------"""
@ -179,7 +180,7 @@ class Admin(commands.Cog):
for emoji in emojis: for emoji in emojis:
await message.add_reaction(emoji) await message.add_reaction(emoji)
except discord.errors.NotFound: except discord.errors.NotFound:
await ctx.send(Texts('utils').get("Unable to find the message"), await ctx.send(Texts('utils', ctx).get("Unable to find the message"),
delete_after=5) delete_after=5)
@_react.command(name='clear') @_react.command(name='clear')
@ -189,7 +190,7 @@ class Admin(commands.Cog):
message_id) message_id)
await message.clear_reactions() await message.clear_reactions()
except discord.errors.NotFound: except discord.errors.NotFound:
await ctx.send(Texts('utils').get("Unable to find the message"), await ctx.send(Texts('utils', ctx).get("Unable to find the message"),
delete_after=5) delete_after=5)
"""---------------------------------------------------------------------""" """---------------------------------------------------------------------"""
@ -206,7 +207,7 @@ class Admin(commands.Cog):
message_id) message_id)
await message.delete() await message.delete()
except (discord.errors.NotFound, discord.errors.Forbidden): except (discord.errors.NotFound, discord.errors.Forbidden):
await ctx.send(Texts('utils').get("Unable to find the message"), await ctx.send(Texts('utils', ctx).get("Unable to find the message"),
delete_after=5) delete_after=5)
@_delete.command(name='from', aliases=['to', 'in']) @_delete.command(name='from', aliases=['to', 'in'])
@ -222,7 +223,7 @@ class Admin(commands.Cog):
message_id) message_id)
await message.delete() await message.delete()
except (discord.errors.NotFound, discord.errors.Forbidden): except (discord.errors.NotFound, discord.errors.Forbidden):
await ctx.send(Texts('utils').get("Unable to find the message"), await ctx.send(Texts('utils', ctx).get("Unable to find the message"),
delete_after=5) delete_after=5)
"""---------------------------------------------------------------------""" """---------------------------------------------------------------------"""
@ -276,7 +277,7 @@ class Admin(commands.Cog):
if ctx.invoked_subcommand is None: if ctx.invoked_subcommand is None:
warns_list, warns = await self.get_warn(ctx) warns_list, warns = await self.get_warn(ctx)
e = discord.Embed( e = discord.Embed(
title=f"{warns.count()} {Texts('admin').get('last warns')}: ", title=f"{warns.count()} {Texts('admin', ctx).get('last warns')}: ",
description=warns_list description=warns_list
) )
@ -288,7 +289,7 @@ class Admin(commands.Cog):
member = await ctx.guild.fetch_member(member.id) member = await ctx.guild.fetch_member(member.id)
if not member: if not member:
return await ctx.send( return await ctx.send(
Texts('utils').get("Unable to find the user...") Texts('utils', ctx).get("Unable to find the user...")
) )
def check(pld: discord.RawReactionActionEvent): def check(pld: discord.RawReactionActionEvent):
@ -301,15 +302,15 @@ class Admin(commands.Cog):
if warns.count() >= 3: if warns.count() >= 3:
e = discord.Embed( e = discord.Embed(
title=Texts('admin').get('More than 2 warns'), title=Texts('admin', ctx).get('More than 2 warns'),
description=f"{member.mention} " description=f"{member.mention} "
+ Texts('admin').get('has more than 2 warns') + Texts('admin', ctx).get('has more than 2 warns')
) )
e.add_field( e.add_field(
name='__Actions__', name='__Actions__',
value=':one: kick\n' value=':one: kick\n'
':two: ban\n' ':two: ban\n'
':three: ' + Texts('admin').get('ignore') ':three: ' + Texts('admin', ctx).get('ignore')
) )
choice = await ctx.send(embed=e) choice = await ctx.send(embed=e)
@ -325,7 +326,7 @@ class Admin(commands.Cog):
) )
except asyncio.TimeoutError: except asyncio.TimeoutError:
return await ctx.send( return await ctx.send(
Texts('admin').get('Took too long. Aborting.') Texts('admin', ctx).get('Took too long. Aborting.')
) )
finally: finally:
await choice.delete() await choice.delete()
@ -338,7 +339,7 @@ class Admin(commands.Cog):
content=f"{ctx.prefix}" content=f"{ctx.prefix}"
f"kick " f"kick "
f"{member} " f"{member} "
f"{Texts('admin').get('More than 2 warns')}" f"{Texts('admin', ctx).get('More than 2 warns')}"
) )
return await alt_ctx.command.invoke(alt_ctx) return await alt_ctx.command.invoke(alt_ctx)
@ -350,15 +351,15 @@ class Admin(commands.Cog):
content=f"{ctx.prefix}" content=f"{ctx.prefix}"
f"ban " f"ban "
f"{member} " f"{member} "
f"{Texts('admin').get('More than 2 warns')}" f"{Texts('admin', ctx).get('More than 2 warns')}"
) )
return await alt_ctx.command.invoke(alt_ctx) return await alt_ctx.command.invoke(alt_ctx)
await self.add_warn(ctx, member, reason) await self.add_warn(ctx, member, reason)
await ctx.send( await ctx.send(
content=f"{member.mention} " content=f"{member.mention} "
f"**{Texts('admin').get('got a warn')}**" f"**{Texts('admin', ctx).get('got a warn')}**"
f"\n**{Texts('admin').get('Reason')}:** `{reason}`" f"\n**{Texts('admin', ctx).get('Reason')}:** `{reason}`"
) )
@_warn.command(name='remove', aliases=['revoke']) @_warn.command(name='remove', aliases=['revoke'])
@ -366,15 +367,15 @@ class Admin(commands.Cog):
warn = self.bot.engine.query(Warn).filter(Warn.id == warn_id).one() warn = self.bot.engine.query(Warn).filter(Warn.id == warn_id).one()
self.bot.engine.delete(warn) self.bot.engine.delete(warn)
await ctx.send(f"{Texts('admin').get('Warn with id')} `{warn_id}`" await ctx.send(f"{Texts('admin', ctx).get('Warn with id')} `{warn_id}`"
f" {Texts('admin').get('successfully removed')}") f" {Texts('admin', ctx).get('successfully removed')}")
@_warn.command(name='show', aliases=['list']) @_warn.command(name='show', aliases=['list'])
async def _warn_show(self, ctx: commands.Context, member: discord.Member): async def _warn_show(self, ctx: commands.Context, member: discord.Member):
warns_list, warns = await self.get_warn(ctx, member) warns_list, warns = await self.get_warn(ctx, member)
e = discord.Embed( e = discord.Embed(
title=f"{warns.count()} {Texts('admin').get('last warns')}: ", title=f"{warns.count()} {Texts('admin', ctx).get('last warns')}: ",
description=warns_list description=warns_list
) )
@ -386,35 +387,36 @@ class Admin(commands.Cog):
warn.reason = reason warn.reason = reason
self.bot.engine.commit() self.bot.engine.commit()
await ctx.send(f"{Texts('admin').get('Warn with id')} `{warn_id}`" await ctx.send(f"{Texts('admin', ctx).get('Warn with id')} `{warn_id}`"
f" {Texts('admin').get('successfully edited')}") f" {Texts('admin', ctx).get('successfully edited')}")
"""---------------------------------------------------------------------""" """---------------------------------------------------------------------"""
@commands.command(name='language', aliases=['lang', 'langue', 'langage']) @commands.command(name='language', aliases=['lang', 'langue', 'langage'])
async def _language(self, ctx: commands.Context, locale): async def _language(self, ctx: commands.Context, locale: str):
query = """ available = self.bot.engine\
SELECT locale .query(Lang.value)\
FROM lang .filter(Lang.key == 'available')\
WHERE key = 'available' .one()[0]\
""" .split(', ')
async with self.bot.engine.begin() as con: if locale.lower() not in available:
await ctx.trigger_typing() await ctx.send(Texts('admin', ctx).get('Unable to find this language'))
available = list(await con.fetchrow(query)) else:
current = self.bot.engine\
.query(Lang)\
.filter(Lang.key == str(ctx.guild.id))
if str(locale) in available: if current.count() > 0:
query = """ current = current.one()
IF EXISTS(SELECT * FROM lang WHERE key = $1 ) current.value = locale.lower()
then self.bot.engine.commit()
UPDATE lang else:
SET locale = $2 new_row = Lang(key=str(ctx.guild.id), value=locale.lower())
WHERE key = $1 self.bot.engine.add(new_row)
ELSE self.bot.engine.commit()
INSERT INTO lang (key, locale)
VALUES ($1, $2) await ctx.send(Texts('admin', ctx).get('Language changed successfully'))
"""
await con.fetch(query, str(ctx.guild.id), str(locale))
def setup(bot: TuxBot): def setup(bot: TuxBot):

View file

@ -68,17 +68,17 @@ class Basics(commands.Cog):
with proc.oneshot(): with proc.oneshot():
mem = proc.memory_full_info() mem = proc.memory_full_info()
e = discord.Embed( e = discord.Embed(
title=f"{Texts('basics').get('Information about TuxBot')}", title=f"{Texts('basics', ctx).get('Information about TuxBot')}",
color=0x89C4F9) color=0x89C4F9)
e.add_field( e.add_field(
name=f"__{Texts('basics').get('Latest changes')}__", name=f"__{Texts('basics', ctx).get('Latest changes')}__",
value=self._latest_commits(), value=self._latest_commits(),
inline=False) inline=False)
e.add_field( e.add_field(
name=f"__:busts_in_silhouette: " name=f"__:busts_in_silhouette: "
f"{Texts('basics').get('Development')}__", f"{Texts('basics', ctx).get('Development')}__",
value=f"**Romain#5117:** [git](https://git.gnous.eu/Romain)\n" value=f"**Romain#5117:** [git](https://git.gnous.eu/Romain)\n"
f"**Outout#4039:** [git](https://git.gnous.eu/mael)\n", f"**Outout#4039:** [git](https://git.gnous.eu/mael)\n",
inline=True inline=True
@ -92,41 +92,41 @@ class Basics(commands.Cog):
e.add_field( e.add_field(
name="__:gear: Usage__", name="__:gear: Usage__",
value=f"**{humanize.naturalsize(mem.rss)}** " value=f"**{humanize.naturalsize(mem.rss)}** "
f"{Texts('basics').get('physical memory')}\n" f"{Texts('basics', ctx).get('physical memory')}\n"
f"**{humanize.naturalsize(mem.vms)}** " f"**{humanize.naturalsize(mem.vms)}** "
f"{Texts('basics').get('virtual memory')}\n", f"{Texts('basics', ctx).get('virtual memory')}\n",
inline=True inline=True
) )
e.add_field( e.add_field(
name=f"__{Texts('basics').get('Servers count')}__", name=f"__{Texts('basics', ctx).get('Servers count')}__",
value=str(len(self.bot.guilds)), value=str(len(self.bot.guilds)),
inline=True inline=True
) )
e.add_field( e.add_field(
name=f"__{Texts('basics').get('Channels count')}__", name=f"__{Texts('basics', ctx).get('Channels count')}__",
value=str(len([_ for _ in self.bot.get_all_channels()])), value=str(len([_ for _ in self.bot.get_all_channels()])),
inline=True inline=True
) )
e.add_field( e.add_field(
name=f"__{Texts('basics').get('Members count')}__", name=f"__{Texts('basics', ctx).get('Members count')}__",
value=str(len([_ for _ in self.bot.get_all_members()])), value=str(len([_ for _ in self.bot.get_all_members()])),
inline=True inline=True
) )
e.add_field( e.add_field(
name=f"__:file_folder: {Texts('basics').get('Files')}__", name=f"__:file_folder: {Texts('basics', ctx).get('Files')}__",
value=str(files), value=str(files),
inline=True inline=True
) )
e.add_field( e.add_field(
name=f"__¶ {Texts('basics').get('Lines')}__", name=f"__¶ {Texts('basics', ctx).get('Lines')}__",
value=str(lines), value=str(lines),
inline=True inline=True
) )
e.add_field( e.add_field(
name=f"__:link: {Texts('basics').get('Links')}__", name=f"__:link: {Texts('basics', ctx).get('Links')}__",
value="[tuxbot.gnous.eu](https://tuxbot.gnous.eu/) " value="[tuxbot.gnous.eu](https://tuxbot.gnous.eu/) "
"| [gnous.eu](https://gnous.eu/) " "| [gnous.eu](https://gnous.eu/) "
f"| [{Texts('basics').get('Invite')}](https://discordapp.com/oauth2/authorize?client_id=301062143942590465&scope=bot&permissions=268749888)", f"| [{Texts('basics').get('Invite')}](https://discordapp.com/oauth2/authorize?client_id=301062143942590465&scope=bot&permissions=268749888)",
@ -142,7 +142,7 @@ class Basics(commands.Cog):
@commands.command(name='credits', aliases=['contributors', 'authors']) @commands.command(name='credits', aliases=['contributors', 'authors'])
async def _credits(self, ctx: commands.Context): async def _credits(self, ctx: commands.Context):
e = discord.Embed( e = discord.Embed(
title=Texts('basics').get('Contributors'), title=Texts('basics', ctx).get('Contributors'),
color=0x36393f color=0x36393f
) )

View file

@ -28,7 +28,7 @@ class Utility(commands.Cog):
ip = socket.getaddrinfo(addr, None, socket.AF_INET6)[1][4][0] ip = socket.getaddrinfo(addr, None, socket.AF_INET6)[1][4][0]
except socket.gaierror: except socket.gaierror:
return await ctx.send( return await ctx.send(
Texts('utility').get('ipv6 not available')) Texts('utility', ctx).get('ipv6 not available'))
else: else:
ip = socket.gethostbyname(addr) ip = socket.gethostbyname(addr)
@ -37,19 +37,19 @@ class Utility(commands.Cog):
if response.get('status') == 'success': if response.get('status') == 'success':
e = discord.Embed( e = discord.Embed(
title=f"{Texts('utility').get('Information for')} " title=f"{Texts('utility', ctx).get('Information for')} "
f"``{addr}`` *`({response.get('query')})`*", f"``{addr}`` *`({response.get('query')})`*",
color=0x5858d7 color=0x5858d7
) )
e.add_field( e.add_field(
name=Texts('utility').get('Belongs to :'), name=Texts('utility', ctx).get('Belongs to :'),
value=response.get('org', 'N/A'), value=response.get('org', 'N/A'),
inline=False inline=False
) )
e.add_field( e.add_field(
name=Texts('utility').get('Is located at :'), name=Texts('utility', ctx).get('Is located at :'),
value=response.get('city', 'N/A'), value=response.get('city', 'N/A'),
inline=True inline=True
) )
@ -68,7 +68,7 @@ class Utility(commands.Cog):
await ctx.send(embed=e) await ctx.send(embed=e)
else: else:
await ctx.send( await ctx.send(
content=f"{Texts('utility').get('info not available')}" content=f"{Texts('utility', ctx).get('info not available')}"
f"``{response.get('query')}``") f"``{response.get('query')}``")
"""---------------------------------------------------------------------""" """---------------------------------------------------------------------"""
@ -82,7 +82,7 @@ class Utility(commands.Cog):
async with self.bot.session.get(addr) as s: async with self.bot.session.get(addr) as s:
await ctx.trigger_typing() await ctx.trigger_typing()
e = discord.Embed( e = discord.Embed(
title=f"{Texts('utility').get('Headers of')} {addr}", title=f"{Texts('utility', ctx).get('Headers of')} {addr}",
color=0xd75858 color=0xd75858
) )
e.add_field(name="Status", value=s.status, inline=True) e.add_field(name="Status", value=s.status, inline=True)
@ -96,7 +96,7 @@ class Utility(commands.Cog):
await ctx.send(embed=e) await ctx.send(embed=e)
except aiohttp.client_exceptions.ClientConnectorError: except aiohttp.client_exceptions.ClientConnectorError:
await ctx.send(f"{Texts('utility').get('Cannot connect to host')} " await ctx.send(f"{Texts('utility', ctx).get('Cannot connect to host')} "
f"{addr}") f"{addr}")
"""---------------------------------------------------------------------""" """---------------------------------------------------------------------"""
@ -104,8 +104,8 @@ class Utility(commands.Cog):
@commands.command(name='git', aliases=['sources', 'source', 'github']) @commands.command(name='git', aliases=['sources', 'source', 'github'])
async def _git(self, ctx): async def _git(self, ctx):
e = discord.Embed( e = discord.Embed(
title=Texts('utility').get('git repo'), title=Texts('utility', ctx).get('git repo'),
description=Texts('utility').get('git text'), description=Texts('utility', ctx).get('git text'),
colour=0xE9D460 colour=0xE9D460
) )
e.set_author( e.set_author(

View file

@ -1,10 +1,15 @@
import gettext import gettext
import config import config
from .models.lang import Lang
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from discord.ext import commands
class Texts: class Texts:
def __init__(self, base: str = 'base'): def __init__(self, base: str = 'base', ctx: commands.Context = None):
self.locale = config.locale self.locale = self.get_locale(ctx)
self.base = base self.base = base
def get(self, text: str) -> str: def get(self, text: str) -> str:
@ -15,3 +20,25 @@ class Texts:
def set(self, lang: str): def set(self, lang: str):
self.locale = lang self.locale = lang
@staticmethod
def get_locale(ctx):
engine = create_engine(config.postgresql)
Session = sessionmaker()
Session.configure(bind=engine)
session = Session()
if ctx is not None:
current = session\
.query(Lang.value)\
.filter(Lang.key == str(ctx.guild.id))
if current.count() > 0:
return current.one()[0]
default = session\
.query(Lang.value)\
.filter(Lang.key == 'default')\
.one()[0]
return default

13
cogs/utils/models/lang.py Normal file
View file

@ -0,0 +1,13 @@
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String
Base = declarative_base()
class Lang(Base):
__tablename__ = 'lang'
key = Column(String, primary_key=True)
value = Column(String)
def __repr__(self):
return "<Lang(key='%s', locale='%s')>" % (self.key, self.value)

View file

@ -53,3 +53,9 @@ msgstr ""
msgid "successfully edited" msgid "successfully edited"
msgstr "" msgstr ""
msgid "Unable to find this language"
msgstr ""
msgid "Language changed successfully"
msgstr ""

View file

@ -53,3 +53,9 @@ msgstr "a été enlevé avec succes"
msgid "successfully edited" msgid "successfully edited"
msgstr "a été édité avec succes" msgstr "a été édité avec succes"
msgid "Unable to find this language"
msgstr "Impossible de trouver cette langue"
msgid "Language changed successfully"
msgstr "Langue changée avec succès"