Beispiel #1
0
async def control_register(ctx, *args):
    if " ".join(args) == "From AF With Love!":
        DB.update_member(ctx.author.id, str(ctx.author), True)
        return await ctx.send(
            "✅ You have been registered and are allowed to issue commands privately! 🥳"
        )
    return await ctx.send(MESSAGES["command_failed"])
Beispiel #2
0
async def control_mention_set(ctx, kind: str, division: str, role: str):
    for guild_role in ctx.guild.roles:
        if guild_role.mention == role:
            if not guild_role.mentionable:
                return await ctx.send(
                    f"❌ Unable to use {role=}, because this role is not globally mentionable!"
                )
            DB.add_role_mapping_entry(kind, ctx.channel.id,
                                      DIVISION_MAPPING[division],
                                      guild_role.id)
            return await ctx.send(
                f"✅ Success! For {division} epics I will mention {guild_role.mention}"
            )
    return await ctx.send(MESSAGES["command_failed"])
Beispiel #3
0
async def control_notify(ctx, kind):
    if kind == "epic":
        if DB.add_notification_channel(ctx.guild.id, ctx.channel.id, kind):
            return await ctx.send(
                MESSAGES["notifications_set"].format("epic battles"))
    elif kind == "events":
        if DB.add_notification_channel(ctx.guild.id, ctx.channel.id, kind):
            return await ctx.send(
                MESSAGES["notifications_set"].format("eLatvia's events"))
    elif kind == "empty":
        if DB.add_notification_channel(ctx.guild.id, ctx.channel.id, kind):
            return await ctx.send(
                MESSAGES["notifications_set"].format("empty medals"))
    return await ctx.send(MESSAGES["nothing_to_do"])
Beispiel #4
0
async def empty(ctx, division, minutes: int = 0):
    _process_member(ctx.message.author)
    if not (ctx.channel.id == 603527159109124096
            or DB.get_member(ctx.message.author.id).get("pm_is_allowed")):
        return await ctx.send("Currently unavailable!")
    try:
        div = int(division)
    except ValueError:
        try:
            div = dict(D1=1, D2=3, D3=3, D4=4, Air=11)[division.title()]
        except (AttributeError, KeyError):
            return await ctx.send(
                "First argument must be a value from: 1, d1, 2, d2, 3, d3, 4, d4, 11, air!"
            )
    s_div = {1: "D1", 2: "D2", 3: "D3", 4: "D4", 11: "Air"}[div]
    embed = Embed(
        title=f"Possibly empty {s_div} medals",
        description=
        "'Empty' medals are being guessed based on the division wall. Expect false-positives!",
    )
    for kind, div_div, data in check_battles(get_battle_page().get("battles")):
        if kind == "empty" and div_div == div and data[
                "round_time_s"] >= minutes * 60:
            embed.add_field(
                name=
                f"**Battle for {data['region']} {' '.join(data['sides'])}**",
                value=
                f"[R{data['zone_id']} | Time {data['round_time']}]({data['url']})",
            )
            if len(embed.fields) >= 10:
                return await ctx.send(embed=embed)
    if embed.fields:
        return await ctx.send(embed=embed)
    else:
        return await ctx.send(f"No empty {s_div} medals found")
Beispiel #5
0
async def control_order_set(ctx, battle_id, side):
    if not DB.get_battle_order(battle_id):
        side_id = None
        try:
            side_id = COUNTRIES[int(side)].id
        except (ValueError, KeyError):
            try:
                side_id = [
                    c for c in COUNTRIES.values()
                    if side.lower() in repr(c).lower()
                ][0].id
            except IndexError:
                return await ctx.send(MESSAGES["command_failed"])
        DB.set_battle_order(battle_id, side_id)
        return await ctx.send(
            f"✅ Order has been set! {COUNTIRES[side_id].name} must win")
    return await ctx.send(MESSAGES["nothing_to_do"])
Beispiel #6
0
async def control_unnotify(ctx, kind):
    if DB.remove_kind_notification_channel(kind, ctx.channel.id):
        if kind == "epic":
            return await ctx.send(
                MESSAGES["notifications_unset"].format("epic battles"))
        if kind == "events":
            return await ctx.send(MESSAGES["notifications_unset"].format(
                "eLatvia's notifications"))
        if kind == "empty":
            return await ctx.send(
                MESSAGES["notifications_unset"].format("empty medals"))
    return await ctx.send(MESSAGES["command_failed"])
Beispiel #7
0
    async def report_rss_events(self):
        await self.wait_until_ready()
        feed_response = None
        while not self.is_closed():
            try:
                for country in COUNTRIES.values():
                    latest_ts = DB.get_rss_feed_timestamp(country.id)
                    rss_link = f"https://www.erepublik.com/en/main/news/military/all/{country.link}/1/rss"
                    feed_response = requests.get(rss_link)
                    feed_response.raise_for_status()
                    for entry in reversed(
                            feedparser.parse(feed_response.text).entries):
                        entry_ts = time.mktime(entry["published_parsed"])
                        entry_link = entry["link"]
                        # Check if event timestamp is after latest processed event for country
                        if entry_ts > latest_ts:
                            DB.set_rss_feed_timestamp(country.id, entry_ts)
                            title = text = ""
                            msg = entry["summary"]
                            dont_send = False
                            for kind in events:
                                match = kind.regex.search(msg)
                                if match:
                                    values = match.groupdict()
                                    # Special case for Dictator/Liberation wars
                                    if "invader" in values and not values[
                                            "invader"]:
                                        values["invader"] = values["defender"]

                                    # Special case for resource concession
                                    if "link" in values:
                                        __link = values["link"]
                                        entry_link = __link if __link.startswith(
                                            "http"
                                        ) else f"https://www.erepublik.com{__link}"
                                        logger.debug(
                                            kind.format.format(**dict(
                                                match.groupdict(), **{
                                                    "current_country":
                                                    country.name
                                                })))
                                        logger.debug(entry_link)
                                    is_latvia = country.id == 71
                                    has_latvia = any("Latvia" in v
                                                     for v in values.values())
                                    if is_latvia or has_latvia:
                                        text = kind.format.format(**dict(
                                            match.groupdict(), **
                                            {"current_country": country.name}))
                                        title = kind.name
                                    else:
                                        dont_send = True
                                    break
                            else:
                                logger.warning(
                                    f"Unable to parse: {str(entry)}")
                                continue

                            if dont_send:
                                continue

                            entry_datetime = datetime.datetime.fromtimestamp(
                                entry_ts, pytz.timezone("US/Pacific"))
                            embed = discord.Embed(title=title,
                                                  url=entry_link,
                                                  description=text)
                            embed.set_author(
                                name=country.name,
                                icon_url=
                                f"https://www.erepublik.com/images/flags/L/{country.link}.gif"
                            )
                            embed.set_footer(
                                text=
                                f"{entry_datetime.strftime('%F %T')} (eRepublik time)"
                            )

                            logger.debug(f"Message sent: {text}")
                            for channel_id in DB.get_kind_notification_channel_ids(
                                    "events"):
                                await self.get_channel(channel_id).send(
                                    embed=embed)
            except Exception as e:
                logger.error("eRepublik event reader ran into a problem!",
                             exc_info=e)
                try:
                    with open(f"debug/{timestamp()}.rss", "w") as f:
                        f.write(feed_response.text)
                except (NameError, AttributeError):
                    logger.error("There was no Response object!", exc_info=e)
            finally:
                await asyncio.sleep((timestamp() // 300 + 1) * 300 -
                                    timestamp())
Beispiel #8
0
    async def report_battle_events(self):
        await self.wait_until_ready()
        while not self.is_closed():
            try:
                r = get_battle_page()
                if not isinstance(r.get("battles"), dict):
                    sleep_seconds = r.get("last_updated") + 60 - timestamp()
                    await asyncio.sleep(
                        sleep_seconds if sleep_seconds > 0 else 0)
                    continue

                desc = "'Empty' medals are being guessed based on the division wall. Expect false-positives!"
                empty_divisions = {
                    1:
                    discord.Embed(
                        title="Possibly empty **__last-minute__ D1** medals",
                        description=desc),
                    2:
                    discord.Embed(
                        title="Possibly empty **__last-minute__ D2** medals",
                        description=desc),
                    3:
                    discord.Embed(
                        title="Possibly empty **__last-minute__ D3** medals",
                        description=desc),
                    4:
                    discord.Embed(
                        title="Possibly empty **__last-minute__ D4** medals",
                        description=desc),
                    11:
                    discord.Embed(
                        title="Possibly empty **__last-minute__ Air** medals",
                        description=desc),
                }
                for kind, div, data in check_battles(r.get("battles")):
                    if kind == "epic" and not DB.check_epic(data["div_id"]):
                        embed_data = dict(
                            title=" ".join(
                                data["extra"]["intensity_scale"].split(
                                    "_")).title(),
                            url=data["url"],
                            description=
                            f"Epic battle {' vs '.join(data['sides'])}!\nBattle for {data['region']}, Round {data['zone_id']}",
                            footer=f"Round time {data['round_time']}",
                        )
                        embed = discord.Embed.from_dict(embed_data)
                        logger.debug(f"{embed_data=}")
                        for channel_id in DB.get_kind_notification_channel_ids(
                                "epic"):
                            if role_id := DB.get_role_id_for_channel_division(
                                    kind="epic",
                                    channel_id=channel_id,
                                    division=div):
                                await self.get_channel(channel_id).send(
                                    f"<@&{role_id}> epic battle detected!",
                                    embed=embed)
                            else:
                                await self.get_channel(channel_id).send(
                                    embed=embed)
                        DB.add_epic(data["div_id"])

                    if kind == "empty" and data[
                            "round_time_s"] >= 85 * 60 and not DB.check_empty_medal(
                                data["div_id"]):
                        empty_divisions[div].add_field(
                            name=
                            f"**Battle for {data['region']} {' '.join(data['sides'])}**",
                            value=
                            f"[R{data['zone_id']} | Time {data['round_time']}]({data['url']})"
                        )
                        DB.add_empty_medal(data["div_id"])
                for d, e in empty_divisions.items():
                    if e.fields:
                        for channel_id in DB.get_kind_notification_channel_ids(
                                "empty"):
                            if role_id := DB.get_role_id_for_channel_division(
                                    kind="empty",
                                    channel_id=channel_id,
                                    division=d):
                                await self.get_channel(channel_id).send(
                                    f"<@&{role_id}> empty medals in late rounds!",
                                    embed=e)
                            else:
                                await self.get_channel(channel_id).send(embed=e
                                                                        )
                sleep_seconds = r.get("last_updated") + 60 - timestamp()
                await asyncio.sleep(sleep_seconds if sleep_seconds > 0 else 0)
Beispiel #9
0
import feedparser
import pytz
import requests
from constants import events
from erepublik.constants import COUNTRIES

from dbot.base import ADMIN_ID, DB, DB_NAME, DEFAULT_CHANNEL_ID, DISCORD_TOKEN, PRODUCTION, logger
from dbot.bot_commands import bot
from dbot.utils import check_battles, get_battle_page, timestamp

if PRODUCTION:
    logger.warning("Production mode enabled!")
    logger.setLevel(logging.INFO)
    _ts = int(time.time())
    for c_id in COUNTRIES.keys():
        DB.set_rss_feed_timestamp(c_id, _ts)
    del _ts

logger.debug(
    f"Active configs:\nDISCORD_TOKEN='{DISCORD_TOKEN}'\nDEFAULT_CHANNEL_ID='{DEFAULT_CHANNEL_ID}'\nADMIN_ID='{ADMIN_ID}'\nDB_NAME='{DB_NAME}'"
)


class MyClient(discord.Client):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # create the background task and run it in the background
        self.last_event_timestamp = timestamp()

    async def on_ready(self):
        logger.info("Client running")
Beispiel #10
0
async def control_mention_remove(ctx, kind: str, division: str):
    if DB.remove_role_mapping(kind, ctx.channel.id,
                              DIVISION_MAPPING[division]):
        return await ctx.send(
            f"✅ I won't mention here any role about {division} events!")
    return await ctx.send(MESSAGES["nothing_to_do"])
Beispiel #11
0
async def control(ctx: commands.Context, command: str, *args):
    _process_member(ctx.message.author)
    if command == "register":
        return await control_register(ctx, *args)
    if command in ["notify", "unnotify"]:
        if ctx.channel.type == ChannelType.private:
            return await ctx.send(MESSAGES["not_in_pm"])
        if not ctx.author.guild_permissions.administrator:
            return await ctx.send(MESSAGES["not_admin"])
        if not args:
            return await ctx.send(
                f"❌ Please provide what kind of notifications You would like to {'en' if command == 'notify' else 'dis'}able! Currently available: {', '.join(NOTIFICATION_KINDS)}"
            )
        kind = str(args[0]).lower()
        if kind not in NOTIFICATION_KINDS:
            return await ctx.send(
                f'❌ Notification {kind=} is unknown! Currently available: {", ".join(NOTIFICATION_KINDS)}'
            )
        if command == "notify":
            return await control_notify(ctx, kind)
        if command == "unnotify":
            return await control_unnotify(ctx, kind)

    if command == "mention":
        if ctx.channel.type == ChannelType.private:
            return await ctx.send(MESSAGES["not_in_pm"])
        if not ctx.author.guild_permissions.administrator:
            return await ctx.send(MESSAGES["not_admin"])
        if not args or not 3 <= len(args) <= 4:
            return await ctx.send(
                MESSAGES["mention_help"].format(command=command))

        try:
            kind, action, division, *role = args
            if role:
                role = role[0]
            kind = str(kind).lower()
            if ctx.channel.id not in DB.get_kind_notification_channel_ids(
                    kind):
                return await ctx.send(MESSAGES["only_registered_channels"])
            if kind not in ("epic", "empty"):
                return await ctx.send(
                    f"❌ {kind=} doesn't support division mentioning!")

            if action not in ("set", "remove"):
                return await ctx.send(
                    MESSAGES["mention_help"].format(command=command))
            action = str(action).lower()
            division = str(division).title()
            if division not in DIVISION_MAPPING:
                await ctx.send(
                    f"❌ Unknown {division=}! Available divisions: {', '.join(d.title() for d in DIVISION_MAPPING.keys())}"
                )
                return await ctx.send(
                    MESSAGES["mention_info"].format(kind=kind))

            if action == "set":
                return await control_mention_set(ctx, kind, division, role)
            if action == "remove":
                return await control_mention_remove(ctx, kind, division)
        except Exception as e:
            logger.warning(str(e), exc_info=e, stacklevel=3)
            return await ctx.send(
                MESSAGES["mention_help"].format(command=command))

    if command == "exit":
        if ctx.author.id == ADMIN_ID:
            await ctx.send(f"{ctx.author.mention} Bye!")
            sys.exit(0)
    return await ctx.send(f"❌ Unknown {command=}!")
Beispiel #12
0
def _process_member(member):
    if not DB.get_member(member.id):
        DB.add_member(member.id, str(member))
Beispiel #13
0
async def control_order_unset(ctx, battle_id):
    if DB.delete_battle_order(battle_id):
        return await ctx.send(f"✅ Order has been unset!")
    return await ctx.send(MESSAGES["nothing_to_do"])