示例#1
0
def API_draw_graph():
    '''
    : text => edges

    $
    curl  "http://127.0.0.1:5000/draw"  -H "Content-Type:text/json"  -d '{"notes": "\n\nA2\n= x3\n: y4\n\nB6\n~ z7\n"}'

    '''
    text = request.get_json(force=True)['notes']
    data = draw_graph(text)

    # None not a collection
    csrft = request.cookies.get('csrft')
    if csrft not in db.database.collection_names():
        csrft = random_string()

    files = [(text, '(client)')]
    notes = N.make_notes(files)
    with db.as_collection(csrft):
        db.collection().remove()  # drops all documents, keeps collection
        N.write_notes_to_database(notes)
        #TODO expire collection

    response = flask.jsonify(**data)
    response.set_cookie('csrft', csrft)
    return response
示例#2
0
    async def remove(self, ctx: commands.Context, imageName: str):
        '''
        removes an image from the favourites list
        '''
        try:
            tokens = imageName.split("/")  # split tokens

            if len(tokens) > 1:
                collection("favouritensfw").delete_one({
                    "server": ctx.guild.id,
                    "imageID": tokens[1],
                    "user": ctx.author.id,
                    "category": tokens[0]
                })
                fullname = f"{tokens[0]}/{tokens[1]}"
            else:
                collection("favouritensfw").delete_one({
                    "server": ctx.guild.id,
                    "imageID": tokens[0],
                    "user": ctx.author.id,
                    "category": ""
                })
                fullname = f"{tokens[0]}"

            await reactOK(ctx)
            await ctx.send(f"`{fullname}` has been removed.", delete_after=10)

        except BaseException:
            await ctx.send("Image not found.")
示例#3
0
    async def custom_list(self, ctx: commands.Context, list_name: str):
        '''
        Displays a list with the given name, opening it for editing.
        Creates a new list if the name doesn't exist.
        Opens a new list if you already have one open
        '''
        if ctx.invoked_subcommand is None:
            if self.active[ctx.channel.id]:
                await self.close(ctx)  # close list

            if L := collection("lists").find_one({
                    "server": ctx.guild.id,
                    "name": list_name
            }):
                self.active[ctx.channel.id] = CustomList(L)

                await self.print_list(ctx)
                await ctx.send(
                    f"You have opened the list `{list_name}`.\nMake sure to `{bot_prefix}list close` to save your list."
                )
            else:
                # New list
                L = {
                    "server": ctx.guild.id,
                    "name": list_name,
                    "author": ctx.author.id,
                    "list": []
                }
                self.active[ctx.channel.id] = CustomList(L)
                collection("lists").insert_one(L)
                await ctx.send(
                    f"You have created the list `{list_name}`.\nMake sure to `{bot_prefix}list close` to save your list."
                )
示例#4
0
    async def remind(self, ctx: commands.context, time: str, *, message: str):
        '''
        Adds a reminder in some amount of time.
        Time should be specified as "1d" "60m" "23h" etc.
        Supports minutes, hours, days.
        Defaults to hours if no unit given.
        '''
        if time[-1] == "d":
            advance = datetime.timedelta(days=float(time[:-1].strip()))
        elif time[-1] == "m":
            advance = datetime.timedelta(minutes=float(time[:-1].strip()))
        elif time[-1] == "h":
            advance = datetime.timedelta(hours=float(time[:-1].strip()))
        else:
            advance = datetime.timedelta(hours=float(time.strip()))

        reminder = {
            "server": ctx.guild is not None,
            "message": message,
            "time": datetime.datetime.utcnow() + advance,
            "user": ctx.author.id,
            "channel": ctx.channel.id if ctx.guild else 0,
            "jumpurl": ctx.message.jump_url
        }

        collection("reminders").insert_one(reminder)

        await ctx.send(
            f"I will remind you on {(local_time() + advance).strftime('%B %d %Y at %I:%M %p %Z')}.")
示例#5
0
    async def rename(self, ctx: commands.Context, oldimageName: str,
                     newimageName: str):
        '''
        renames an image in the favourites list
        '''
        tokens = oldimageName.split("/")  # split tokens

        fav = None

        if len(tokens) == 1:
            fav = collection("favouritensfw").find_one({
                "server": ctx.guild.id,
                "imageID": tokens[0],
                "user": ctx.author.id,
                "category": ""
            })
        elif len(tokens) == 2:
            fav = collection("favouritensfw").find_one({
                "server": ctx.guild.id,
                "imageID": tokens[1],
                "user": ctx.author.id,
                "category": tokens[0]
            })
        if fav:
            await self.unfavourite(ctx, oldimageName)
            await self.favourite(ctx, newimageName, fav["URL"])
示例#6
0
def rebuild_user_profiles(guild: discord.Guild):
    '''
    Rebuilds user profiles within a given server
    '''
    logger.info(f"{guild.name}: Scanning for new members...")

    DB_members = collection("users").find({"server": guild.id})

    member_ids = [m["user"] for m in DB_members]

    actual_member_ids = [m.id for m in guild.members]

    # Scan to add new
    for member in guild.members:
        if member.id not in member_ids:
            logger.info(f"New Member Found in {guild.name}")
            collection("users").insert_one(new_user(member))

    # Scan to remove old
    for mem_id in member_ids:
        if mem_id not in actual_member_ids and \
                cfg["Settings"]["development-mode"] == "False":
            logger.info(f"Deleting Old Member Found in {guild.name}")
            collection("users").delete_one({
                "server": guild.id,
                "user": mem_id
            })
示例#7
0
async def inject(ctx: commands.Context, name) -> discord.Emoji:
    '''
    Attempts to inject image into the server's list of emoji,
    returning it afterward
    '''
    if document := collection("emotes").find_one(
            {"name": name, "server": ctx.guild.id}):
        LIMIT = 50

        # UNLOAD EMOJI
        if len(ctx.guild.emojis) >= LIMIT - 1:

            unload = ctx.guild.emojis[0]  # emoji to be unloaded -- oldest one

            if not collection("emotes").find_one(
                    {"name": unload.name, "server": ctx.guild.id}):
                # If not loaded, we must first database it

                # upload the about-to-be-destroyed emoji
                await upload(ctx, unload.name, unload.url, "inline")

            await unload.delete(reason=f"Unloading emoji to make space for {name}")

        # LOAD NEW EMOJI
        async with session.get(document["URL"]) as resp:
            content = await resp.read()

        return await ctx.guild.create_custom_emoji(name=document["name"], image=content, reason=f"Requested by user {ctx.author.display_name}")
示例#8
0
 async def deop(self, ctx, *, member: discord.Member):
     '''
     de-OPs a member
     '''
     collection("users").update_one(ufil(member), {"$set": {"OP": False}})
     op_list.cache_clear()
     await ctx.send(f"{member.display_name} is no longer OP.")
示例#9
0
def text_filter(content: str,
                author: discord.Member,
                guild: discord.Guild) -> bool:
    '''
    Detects banned words in a string
    '''
    spaced_query = unidecode.unidecode(emojiToText(content.lower()))
    # remove non-ascii, attempt to decode unicode,
    # get into formattable form
    query = re.sub(r"\W+", '', spaced_query)  # spaces

    server_words: dict = collection(
        "servers").find_one(guild.id)["global-banned-words"]
    words: dict = collection(
        "users").find_one(ufil(author))["moderation"]["banned-words"]

    words.update(server_words)

    # TODO with python 3.9 -- dictionary union
    for w in words:

        if words[w] < 100 and (
            len(query) > 2 and fuzz.partial_ratio(query, w) >= words[w]) or query == w:
            return True

        for word in spaced_query.split(" "):
            if word[0] == w[0] and word[-1] == w[-1] and fuzz.partial_ratio(word, w) >= words[w]:
                return True
            # checking endcap letters
    return False
示例#10
0
    async def on_ready(self):
        '''
        Reconstruct poll listeners
        '''
        coros = []

        for poll in collection("polls").find():
            logger.info(f"Found poll in channel: {poll['channel']}")
            channel: discord.TextChannel = self.bot.get_channel(
                poll["channel"])

            try:
                msg = await channel.fetch_message(poll["_id"])
            except:
                # msg not found
                collection("polls").delete_one(poll)
                logger.warn("Message deleted for poll, unloading")
                continue

            ctx = await self.bot.get_context(msg)
            author = msg.guild.get_member(poll["author"])

            coros.append(
                self.listen_poll(ctx, msg, poll["timeout"], poll["prompt"],
                                 poll["options"], author))
        await asyncio.gather(*coros)
示例#11
0
    async def poll(self,
                   ctx: commands.Context,
                   prompt: str,
                   timeout: typing.Optional[int] = 60,
                   *options):
        '''
        Creates a poll, with an optional timeout.
        Specify a prompt, and then split options by spaces.
        Timeout less than zero will result in permenantly persistent polls.

        ex. `poll "apples or bananas?" "apples are better" "bananas are the best!"`
        ex. `poll "persistent poll" -1 "option 1" "option 2"`

        Polls automatically time out after 60 minutes of inactivity by default.
        '''
        if len(options) < 36:

            lines = "\n".join(
                [f"{i+1}) {options[i]}" for i in range(len(options))])

            e = discord.Embed(title=f"**__POLL__:\n{prompt}**", color=0xd7342a)

            for i in range(len(options)):
                e.add_field(name=f"{i+1}) {options[i]}: 0",
                            value="No one",
                            inline=False)
            e.set_author(
                name=
                f"{ctx.author.display_name}, react to this post with 🛑 to stop the poll.",
                icon_url=ctx.author.avatar_url)
            e.set_footer(
                text=f"Updated {local_time().strftime('%I:%M:%S %p %Z')}")

            msg = await ctx.send(embed=e)

            reacts = "123456789abcdefghijklmnopqrstuvwxyz"

            # Apply reactions
            for i in range(len(options)):
                await msg.add_reaction(textToEmoji(reacts[i]))
            await msg.add_reaction("🛑")

            collection("polls").insert_one({
                "_id": msg.id,
                "channel": ctx.channel.id,
                "msg": msg.id,
                "prompt": prompt,
                "options": options,
                "timeout": timeout,
                "author": ctx.author.id
            })
            logger.info("Storing poll...")

            await self.listen_poll(ctx, msg, timeout, prompt, options,
                                   ctx.author)

        else:
            await ctx.send(
                "Sorry, you can only choose up to 35 options at a time.")
示例#12
0
 async def optout(self, ctx: commands.Context):
     '''
     Opts out from macros
     '''
     collection("users").update_one({"user": ctx.author.id},
                                    {"$set": {
                                        "macros": False
                                    }})
     await ctx.message.add_reaction("­ЪЉЇ")
示例#13
0
 async def removeannounce(self, ctx: commands.Context):
     '''
     Removes an announcement from the announcement system
     '''
     collection("announcements").delete_one({
         "server": ctx.guild.id,
         "owner": ctx.author.id
     })
     await reactOK(ctx)
     await self.build_announcement_cache()  # rebuild cache
示例#14
0
async def rebuild_weight_table(guild: discord.Guild):
    '''
    Refills the daily member counts according to staleness rules.
    '''
    # Attempt to set default daily count
    server_cfg: dict = collection("servers").find_one(guild.id)

    if server_cfg["default-daily-count"] == 0:
        # Daily member turned off
        for member in guild.members:
            collection("users").update_one(ufil(member),
                                           {"$set": {
                                               "daily-weight": 0
                                           }})
        return

    logger.warning(
        f"{guild.name}: Cache is being reconstructed. This will take a while.")

    # Process staleness
    staleness = server_cfg["daily-member-staleness"]

    if staleness >= 0:
        threshold = datetime.datetime.now() - \
            datetime.timedelta(staleness)

        active_author_ids = set()

        for channel in guild.text_channels:

            active_ids_in_channel = await channel.history(
                limit=None, after=threshold).filter(
                    lambda msg: msg.type == discord.MessageType.default).map(
                        lambda msg: msg.author.id).flatten()

            active_author_ids.update(active_ids_in_channel)
    else:
        active_author_ids = set([member.id for member in guild.members])

    for member in guild.members:
        if (not member.bot or not bool(
            cfg["Settings"]["exclude-bots-from-daily"]))\
                and member.id in active_author_ids:
            daily = server_cfg["default-daily-count"]
        else:
            daily = 0

        collection("users").update_one(ufil(member),
                                       {"$set": {
                                           "daily-weight": daily
                                       }})

    logger.info(
        f"{guild.name}: Rebuilt weights for users in past {staleness} days")
示例#15
0
    async def threat(self, ctx, level: int, *, member: discord.Member):
        '''
        sets a member's threat level
        '''
        collection("users").update_one(
            ufil(member), {"$set": {
                "moderation.threat-level": level
            }})
        threat_list.cache_clear()
        assert member.id in threat_list(ctx.guild.id, level)

        await ctx.send(f"{member.display_name} is now threat level `{level}`")
示例#16
0
    async def build_announcement_cache(self):
        await self.bot.wait_until_ready()
        for g in self.bot.guilds:
            additonal_announcements = [{
                "server": g.id,
                "time": "08:00",
                "announcement": self.dailyannounce,
                "owner": None
            }]

            if d := collection("announcements").find({"server": g.id}):
                self.announcements[g.id] = list(d) + additonal_announcements
            else:
                self.announcements[g.id] = additonal_announcements
示例#17
0
    async def dailyannounce(self, channel: discord.TextChannel):
        '''
        Daily announcement
        '''
        m = await channel.send(
            "Good morning everyone! Today is "
            f"{local_time().strftime('%A, %B %d (Day %j in %Y). Time is %I:%M %p %Z')}. Have a great day."
        )
        '''
        Daily user
        '''
        ctx = await self.bot.get_context(m)

        if luckyperson := channel.guild.get_member(
                await weighted_member_from_server(channel.guild)):

            collection("users").update_one(ufil(luckyperson),
                                           {"$inc": {
                                               "daily-weight": -1
                                           }})

            dailyrole = await role(channel.guild, "Member of the Day")

            for m in dailyrole.members:
                # remove bearer of previous daily role
                roles = m.roles
                roles.remove(dailyrole)
                await m.edit(roles=roles)
            roles = luckyperson.roles
            roles.append(dailyrole)
            await luckyperson.edit(roles=roles)

            await channel.send(
                f"Today's Daily Member is **{luckyperson.display_name}**")

            col = luckyperson.colour

            a = discord.Embed(
                color=col,
                title=f"{luckyperson.display_name}'s Avatar",
                url=str(luckyperson.avatar_url_as(static_format="png")))
            a.set_image(url=luckyperson.avatar_url)
            await ctx.send(embed=a)

            general_cog = self.bot.get_cog("General")
            await general_cog.avatar(ctx, member=luckyperson)

            if not sum_of_weights(channel.guild):
                # rebuild if necessary
                await rebuild_weight_table(channel.guild)
示例#18
0
def edge(arc: Edge, collection=None):
    '''

    # motivation: "x , y < z"
    >>> save(',', 'x', 'y')
    >>> pp(db.find_one({'node': 'y'}))
    {'edges': [[',', 'x', 'y']], 'node': 'y'}

    >>> save('<', 'y', 'z')
    >>> pp(db.find_one({'node': 'y'}))
    {'edges': [[',', 'x', 'y'], ['<', 'y', 'z']], 'node': 'y'}

    >>> pp(db.find_one({'node': 'x'}))
    {'edges': [[',', 'x', 'y']], 'node': 'x'}

    >>> pp(db.find_one({'edge': ','}))
    {'edge': ',', 'nodes': [[',', 'x', 'y']]}

    >>> pp(db.find_one({'edge': '<'}))
    {'edge': '<', 'nodes': [['<', 'y', 'z']]}

    # hyperedge: "a < b where c"
    >>> save('< where', 'a', 'b', 'c')
    >>> pp(db.find_one({'edge': '< where'}))
    {'edge': '< where', 'nodes': [['< where', 'a', 'b', 'c']]}

    '''
    if not collection: collection = db.collection()

    #TODO addToSet duplicates
    for vertex in arc.nodes:
        query = {'node': vertex}
        update = {'$addToSet': {'edges': arc.json}}
        collection.update(query, update, upsert=True)
示例#19
0
def new_user(user: discord.Member):
    '''
    Configures a new user for use, returns a dictionary ready to be updated
    '''

    # Attempt to set default daily count
    server_cfg: dict = collection("servers").find_one(user.guild.id)

    if not user.bot or cfg["Settings"]["exclude-bots-from-daily"] == "False":
        daily = server_cfg["default-daily-count"]
    else:
        daily = 0

    return {
        "user": user.id,
        "server": user.guild.id,
        "last-online": "Now" if str(user.status) == "online" else "Never",
        "OP": False,
        "daily-weight": daily,
        "notify-status": [],
        "identity": "",
        "guessing-game": {
            "highest-streak": 0
        },
        "moderation": {
            "stop-pings": False,
            "stop-images": False,
            "banned-words": {},
            "kick-votes": [],
            "mute-votes": [],
            "threat-level": 0
        },
    }
示例#20
0
    async def createChannel(self, ctx: commands.Context, *, channelname: str):
        '''
        Creates a channel, and gives the user who
        created it full permissions over it.

        If "custom-channel-group" is set in the
        server cfg, it will create the channel there,
        otherwise it will be the same category as where
        the command was called.
        '''
        await ctx.trigger_typing()

        server_cfg: dict = collection("servers").find_one(ctx.guild.id)

        try:
            custom_group = server_cfg["channels"]["custom"]
        except Exception:
            if ctx.channel.category:
                custom_group = ctx.channel.category.id
            else:
                custom_group = 0

        if custom_group:
            # put in specific category
            group = ctx.guild.get_channel(custom_group)
            chn = await group.create_text_channel(channelname)
        else:
            # put in outside
            chn = await ctx.guild.create_text_channel(channelname)

        await chn.set_permissions(ctx.author,
                                  manage_channels=True,
                                  manage_roles=True)
        await ctx.send(f"Channel has been created at {chn.mention}")
示例#21
0
    async def big(self, ctx: commands.Context, page=1):
        '''
        Lists all big emotes in the server, based on page
        '''
        paginator = commands.Paginator(prefix="", suffix="", max_size=200)

        bigemotes = collection("emotes").find(
            {"server": ctx.guild.id, "type": "big"}, {"name": True}).sort(
                "name", 1)

        if not bigemotes:
            return  # empty

        for i in bigemotes:
            paginator.add_line(f"- {i['name']}")

        pages = paginator.pages

        def em_embed(pagenum):
            e = discord.Embed(color=0xd7342a, title=f"__**Big Emotes in {ctx.guild.name}**__")
            e.set_footer(text=f"({pagenum + 1}/{len(pages)})")
            e.description = pages[pagenum]
            return e

        await send_menu(ctx, [em_embed(num) for num in range(len(pages))])
示例#22
0
    async def addannounce(self, ctx: commands.Context, time, *, message):
        '''
        Adds an announcement to the announcement system [24 hr time hh:mm].
        Each user is allowed to have a maximum of one announcement.
        '''
        collection("announcements").update_one(
            {
                "server": ctx.guild.id,
                "owner": ctx.author.id
            }, {"$set": {
                "time": time,
                "announcement": message
            }},
            upsert=True)

        await reactOK(ctx)
        await self.build_announcement_cache()  # rebuild cache
示例#23
0
 def get_all(self, collection="users"):
     collection_ref = db.collection(f'{collection}')
     docs = collection_ref.stream()
     arr = []
     for doc in docs:
         print(f'{doc.id} => {doc.to_dict()}')
         arr.append(doc.to_dict())
     return arr
示例#24
0
def op_list(guild_id: int):
    '''
    Returns list of OPS in a server
    '''
    all_users = collection("users").find(
        {"server": guild_id,
         "OP": True})
    return [u["user"] for u in all_users]
示例#25
0
def threat_list(guild_id: int, threat_level: int):
    '''
    Returns list of threats ids in a given server
    '''
    all_users = collection("users").find(
        {"server": guild_id,
         "moderation.threat-level": {"$gt": threat_level - 1}})
    return [u["user"] for u in all_users]
示例#26
0
def rebuild_server_cfgs(guilds: list):
    '''
    Rebuilds server configuration
    '''
    logger.info("Scanning for new servers...")

    DB_guilds = collection("servers").find()

    guild_ids = [g["_id"] for g in DB_guilds]

    actual_guild_ids = [g.id for g in guilds]

    for guild in guilds:
        if guild.id not in guild_ids:
            logger.info(f"New Server Found: {guild.name}")
            collection("servers").insert_one(new_server(guild))
        rebuild_user_profiles(guild)  # check users too

    for g_id in guild_ids:
        if g_id not in actual_guild_ids and \
                cfg["Settings"]["development-mode"] == "False":
            logger.info("Deleting Old Server & Members")
            collection("servers").delete_one({"_id": g_id})
            collection("users").delete_many({"server": g_id})

    logger.info("Server scan DONE")
示例#27
0
    async def stats(self, ctx: commands.Context):
        '''
        Stats for guess
        '''
        highscore = collection("users").find_one(
            ufil(ctx.author))["guessing-game"]["highest-streak"]
        currscore = self.streak[ctx.author.id]

        await ctx.send(f"**Info for {ctx.author.display_name}**\nCurrent Streak: {currscore}\nHighest Streak: {highscore}")
示例#28
0
 def tag_match(self, tag=[]):
     collection_ref = db.collection(u'users')
     query = collection_ref.where(u'tags', u'array_contains_any', tag)
     result = query.stream()
     arr = []
     for r in result:
         # print(f'{r.id} => {r.to_dict()}')
         arr.append(r.to_dict())
     return arr
示例#29
0
def content_filter(message: discord.Message) -> bool:
    '''
    Checks a message for additional offending characteristics based on member
    Filters pings and images
    '''
    u = collection("users").find_one(ufil(message.author))
    return (u["moderation"]["stop-pings"] and message.mentions) or (
        u["moderation"]["stop-images"] and (
            message.attachments or message.embeds))
示例#30
0
 async def on_member_join(self, member: discord.Member):
     '''
     When a member joins the server.
     '''
     announcements_channel_id = collection(
         "servers").find_one(
             member.guild.id)["channels"]["announcements"]
     if channel := member.guild.get_channel(announcements_channel_id):
         await channel.send(f"Welcome {member.display_name}!")