Add filters, backups, use AwareDateTimeField
This commit is contained in:
parent
fe24fce330
commit
470e999c68
16 changed files with 610 additions and 466 deletions
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"python.formatting.provider": "black"
|
||||
{
|
||||
"python.formatting.provider": "black"
|
||||
}
|
||||
|
|
26
LICENSE
26
LICENSE
|
@ -1,13 +1,13 @@
|
|||
“Commons Clause” License Condition v1.0
|
||||
|
||||
The Software is provided to you by the Licensor under the License, as defined below, subject to the following condition.
|
||||
|
||||
Without limiting other conditions in the License, the grant of rights under the License will not include, and the License does not grant to you, the right to Sell the Software.
|
||||
|
||||
For purposes of the foregoing, “Sell” means practicing any or all of the rights granted to you under the License to provide to third parties, for a fee or other consideration (including without limitation fees for hosting or consulting/ support services related to the Software), a product or service whose value derives, entirely or substantially, from the functionality of the Software. Any license notice or attribution required by the License must also include this Commons Clause License Condition notice.
|
||||
|
||||
Software: JARVIS
|
||||
|
||||
License: Expat License
|
||||
|
||||
Licensor: zevaryx
|
||||
“Commons Clause” License Condition v1.0
|
||||
|
||||
The Software is provided to you by the Licensor under the License, as defined below, subject to the following condition.
|
||||
|
||||
Without limiting other conditions in the License, the grant of rights under the License will not include, and the License does not grant to you, the right to Sell the Software.
|
||||
|
||||
For purposes of the foregoing, “Sell” means practicing any or all of the rights granted to you under the License to provide to third parties, for a fee or other consideration (including without limitation fees for hosting or consulting/ support services related to the Software), a product or service whose value derives, entirely or substantially, from the functionality of the Software. Any license notice or attribution required by the License must also include this Commons Clause License Condition notice.
|
||||
|
||||
Software: JARVIS
|
||||
|
||||
License: Expat License
|
||||
|
||||
Licensor: zevaryx
|
||||
|
|
110
PRIVACY.md
110
PRIVACY.md
|
@ -1,55 +1,55 @@
|
|||
# Privacy Policy
|
||||
Your privacy is important to us. It is JARVIS' policy to respect your privacy and comply with any applicable law and regulation regarding any personal information we may collect about you through our JARVIS bot.
|
||||
|
||||
This policy is effective as of 20 March 2022 and was last updated on 10 March 2022.
|
||||
|
||||
## Information We Collect
|
||||
Information we collect includes both information you knowingly and actively provide us when using or participating in any of our services and promotions, and any information automatically sent by your devices in the course of accessing our products and services.
|
||||
|
||||
## Log Data
|
||||
When you use our JARVIS services, if opted in to usage data collection services, we may collect data in certain cicumstances:
|
||||
|
||||
- Administrative activity (i.e. ban, warn, mute)
|
||||
- Your User ID (either as admin or as the recipient of the activity)
|
||||
- Guild ID of activity origin
|
||||
- Your discriminator at time of activity (bans only)
|
||||
- Your username at time of activity (bans only)
|
||||
- Admin commands
|
||||
- User ID of admin who executes admin command
|
||||
- Reminders
|
||||
- Your User ID
|
||||
- The guild in which the command originated
|
||||
- The channel in which the command originated
|
||||
- Private text entered via the command
|
||||
- Starboard
|
||||
- Message ID of starred message
|
||||
- Channel ID of starred message
|
||||
- Guild ID of origin guild
|
||||
- Automated activity logging
|
||||
- We store no information about users who edit nicknames, join/leave servers, or other related activities. However, this information, if configured by server admins, is relayed into a Discord channel and is not automatically deleted, nor do we have control over this information.
|
||||
- This information is also stored by Discord via their Audit Log, which we also have no control over. Please contact Discord for their own privacy policy and asking about your rights on their platform.
|
||||
|
||||
## Use of Information
|
||||
We use the information we collect to provide, maintain, and improve our services. Common uses where this data may be used includes sending reminders, helping administrate Discord servers, and providing extra utilities into Discord based on user content.
|
||||
|
||||
## Security of Your Personal Information
|
||||
Although we will do our best to protect the personal information you provide to us, we advise that no method of electronic transmission or storage is 100% secure, and no one can guarantee absolute data security. We will comply with laws applicable to us in respect of any data breach.
|
||||
|
||||
## How Long We Keep Your Personal Information
|
||||
We keep your personal information only for as long as we need to. This time period may depend on what we are using your information for, in accordance with this privacy policy. If your personal information is no longer required, we will delete it or make it anonymous by removing all details that identify you.
|
||||
|
||||
## Your Rights and Controlling Your Personal Information
|
||||
You may request access to your personal information, and change what you are okay with us collecting from you. You may also request that we delete your personal identifying information. Please message **zevaryx#5779** on Discord, or join the Discord server at https://discord.gg/4TuFvW5n and ask in there.
|
||||
|
||||
## Limits of Our Policy
|
||||
Our website may link to external sites that are not operated by us (ie. discord.com). Please be aware that we have no control over the content and policies of those sites, and cannot accept responsibility or liability for their respective privacy practices.
|
||||
|
||||
## Changes to This Policy
|
||||
At our discretion, we may change our privacy policy to reflect updates to our business processes, current acceptable practices, or legislative or regulatory changes. If we decide to change this privacy policy, we will post the changes here at the same link by which you are accessing this privacy policy.
|
||||
|
||||
## Contact Us
|
||||
For any questions or concerns regarding your privacy, you may contact us using the following details:
|
||||
|
||||
### Discord
|
||||
#### zevaryx#5779
|
||||
#### https://discord.gg/4TuFvW5n
|
||||
# Privacy Policy
|
||||
Your privacy is important to us. It is JARVIS' policy to respect your privacy and comply with any applicable law and regulation regarding any personal information we may collect about you through our JARVIS bot.
|
||||
|
||||
This policy is effective as of 20 March 2022 and was last updated on 10 March 2022.
|
||||
|
||||
## Information We Collect
|
||||
Information we collect includes both information you knowingly and actively provide us when using or participating in any of our services and promotions, and any information automatically sent by your devices in the course of accessing our products and services.
|
||||
|
||||
## Log Data
|
||||
When you use our JARVIS services, if opted in to usage data collection services, we may collect data in certain cicumstances:
|
||||
|
||||
- Administrative activity (i.e. ban, warn, mute)
|
||||
- Your User ID (either as admin or as the recipient of the activity)
|
||||
- Guild ID of activity origin
|
||||
- Your discriminator at time of activity (bans only)
|
||||
- Your username at time of activity (bans only)
|
||||
- Admin commands
|
||||
- User ID of admin who executes admin command
|
||||
- Reminders
|
||||
- Your User ID
|
||||
- The guild in which the command originated
|
||||
- The channel in which the command originated
|
||||
- Private text entered via the command
|
||||
- Starboard
|
||||
- Message ID of starred message
|
||||
- Channel ID of starred message
|
||||
- Guild ID of origin guild
|
||||
- Automated activity logging
|
||||
- We store no information about users who edit nicknames, join/leave servers, or other related activities. However, this information, if configured by server admins, is relayed into a Discord channel and is not automatically deleted, nor do we have control over this information.
|
||||
- This information is also stored by Discord via their Audit Log, which we also have no control over. Please contact Discord for their own privacy policy and asking about your rights on their platform.
|
||||
|
||||
## Use of Information
|
||||
We use the information we collect to provide, maintain, and improve our services. Common uses where this data may be used includes sending reminders, helping administrate Discord servers, and providing extra utilities into Discord based on user content.
|
||||
|
||||
## Security of Your Personal Information
|
||||
Although we will do our best to protect the personal information you provide to us, we advise that no method of electronic transmission or storage is 100% secure, and no one can guarantee absolute data security. We will comply with laws applicable to us in respect of any data breach.
|
||||
|
||||
## How Long We Keep Your Personal Information
|
||||
We keep your personal information only for as long as we need to. This time period may depend on what we are using your information for, in accordance with this privacy policy. If your personal information is no longer required, we will delete it or make it anonymous by removing all details that identify you.
|
||||
|
||||
## Your Rights and Controlling Your Personal Information
|
||||
You may request access to your personal information, and change what you are okay with us collecting from you. You may also request that we delete your personal identifying information. Please message **zevaryx#5779** on Discord, or join the Discord server at https://discord.gg/4TuFvW5n and ask in there.
|
||||
|
||||
## Limits of Our Policy
|
||||
Our website may link to external sites that are not operated by us (ie. discord.com). Please be aware that we have no control over the content and policies of those sites, and cannot accept responsibility or liability for their respective privacy practices.
|
||||
|
||||
## Changes to This Policy
|
||||
At our discretion, we may change our privacy policy to reflect updates to our business processes, current acceptable practices, or legislative or regulatory changes. If we decide to change this privacy policy, we will post the changes here at the same link by which you are accessing this privacy policy.
|
||||
|
||||
## Contact Us
|
||||
For any questions or concerns regarding your privacy, you may contact us using the following details:
|
||||
|
||||
### Discord
|
||||
#### zevaryx#5779
|
||||
#### https://discord.gg/4TuFvW5n
|
||||
|
|
56
README.rst
56
README.rst
|
@ -1,28 +1,28 @@
|
|||
===========
|
||||
jarvis-core
|
||||
===========
|
||||
|
||||
Core functionality of `JARVIS <https://git.zevaryx.com/stark-industries/j.a.r.v.i.s.>`_
|
||||
|
||||
Modules
|
||||
=======
|
||||
|
||||
``jarvis_core.config``
|
||||
----------------------
|
||||
|
||||
Configuration management
|
||||
|
||||
``jarvis_core.db``
|
||||
------------------
|
||||
|
||||
Database connectors and model implementations
|
||||
|
||||
``jarvis_core.filters``
|
||||
-----------------------
|
||||
|
||||
Regex filters for various functions
|
||||
|
||||
``jarvis_core.util``
|
||||
--------------------
|
||||
|
||||
Quality of life utilities
|
||||
===========
|
||||
jarvis-core
|
||||
===========
|
||||
|
||||
Core functionality of `JARVIS <https://git.zevaryx.com/stark-industries/j.a.r.v.i.s.>`_
|
||||
|
||||
Modules
|
||||
=======
|
||||
|
||||
``jarvis_core.config``
|
||||
----------------------
|
||||
|
||||
Configuration management
|
||||
|
||||
``jarvis_core.db``
|
||||
------------------
|
||||
|
||||
Database connectors and model implementations
|
||||
|
||||
``jarvis_core.filters``
|
||||
-----------------------
|
||||
|
||||
Regex filters for various functions
|
||||
|
||||
``jarvis_core.util``
|
||||
--------------------
|
||||
|
||||
Quality of life utilities
|
||||
|
|
30
TERMS.md
30
TERMS.md
|
@ -1,15 +1,15 @@
|
|||
# Terms Of Use
|
||||
Please just be reasonable do not try to mess with the bot in a malicious way. This includes but is not limited to:
|
||||
|
||||
- Spamming
|
||||
- Flooding
|
||||
- Hacking
|
||||
- DOS Attacks
|
||||
|
||||
## Contact Us
|
||||
For any questions or concerns regarding the terms, feel free to contact us:
|
||||
|
||||
### Discord
|
||||
|
||||
#### zevaryx#5779
|
||||
#### https://discord.gg/4TuFvW5n
|
||||
# Terms Of Use
|
||||
Please just be reasonable do not try to mess with the bot in a malicious way. This includes but is not limited to:
|
||||
|
||||
- Spamming
|
||||
- Flooding
|
||||
- Hacking
|
||||
- DOS Attacks
|
||||
|
||||
## Contact Us
|
||||
For any questions or concerns regarding the terms, feel free to contact us:
|
||||
|
||||
### Discord
|
||||
|
||||
#### zevaryx#5779
|
||||
#### https://discord.gg/4TuFvW5n
|
||||
|
|
26
jarvis_core/db/fields.py
Normal file
26
jarvis_core/db/fields.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
import bson
|
||||
import marshmallow as ma
|
||||
from marshmallow import fields as ma_fields
|
||||
from umongo import fields
|
||||
|
||||
|
||||
class BinaryField(fields.BaseField, ma_fields.Field):
|
||||
default_error_messages = {"invalid": "Not a valid byte sequence."}
|
||||
|
||||
def _serialize(self, value, attr, data, **kwargs):
|
||||
return bytes(value)
|
||||
|
||||
def _deserialize(self, value, attr, data, **kwargs):
|
||||
if not isinstance(value, bytes):
|
||||
self.fail("invalid")
|
||||
return value
|
||||
|
||||
def _serialize_to_mongo(self, obj):
|
||||
return bson.binary.Binary(obj)
|
||||
|
||||
def _deserialize_from_mongo(self, value):
|
||||
return bytes(value)
|
||||
|
||||
|
||||
class RawField(fields.BaseField, ma_fields.Raw):
|
||||
pass
|
|
@ -1,29 +1,19 @@
|
|||
"""JARVIS database models."""
|
||||
from datetime import datetime, timezone
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Any, List
|
||||
|
||||
import marshmallow as ma
|
||||
from umongo import Document, EmbeddedDocument, fields
|
||||
|
||||
from jarvis_core.db import CTC2_INST, JARVIS_INST
|
||||
from jarvis_core.db.models.actions import Ban, Kick, Mute, Unban, Warning
|
||||
from jarvis_core.db.models.modlog import Action, Modlog, Note
|
||||
from jarvis_core.db.models.reddit import (
|
||||
Redditor,
|
||||
RedditorFollow,
|
||||
Subreddit,
|
||||
SubredditFollow,
|
||||
)
|
||||
from jarvis_core.db.models.twitter import TwitterAccount, TwitterFollow
|
||||
|
||||
|
||||
def get_now() -> datetime:
|
||||
"""Get proper timestamp."""
|
||||
return datetime.now(tz=timezone.utc)
|
||||
|
||||
|
||||
class RawField(fields.BaseField, ma.fields.Raw):
|
||||
pass
|
||||
from jarvis_core.db.fields import RawField
|
||||
from jarvis_core.db.models.actions import *
|
||||
from jarvis_core.db.models.backups import *
|
||||
from jarvis_core.db.models.modlog import *
|
||||
from jarvis_core.db.models.reddit import *
|
||||
from jarvis_core.db.models.twitter import *
|
||||
from jarvis_core.db.utils import get_now
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -32,7 +22,7 @@ class Autopurge(Document):
|
|||
channel: int = fields.IntegerField(required=True)
|
||||
delay: int = fields.IntegerField(default=30)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=datetime.now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -42,7 +32,7 @@ class Autoreact(Document):
|
|||
reactions: List[str] = fields.ListField(fields.StringField())
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
thread: bool = fields.BooleanField(default=True)
|
||||
created_at: datetime = fields.DateTimeField(default=datetime.now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -53,6 +43,22 @@ class Config(Document):
|
|||
value: Any = RawField(required=True)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class Filter(Document):
|
||||
"""Regex Filter database object."""
|
||||
|
||||
def _validate_filters(value):
|
||||
for v in value:
|
||||
try:
|
||||
re.compile(v)
|
||||
except re.error:
|
||||
raise ValueError(f"Invalid regex: {v}")
|
||||
|
||||
guild: int = fields.IntegerField(required=True)
|
||||
name: str = fields.StringField(required=True)
|
||||
filters: List[str] = fields.ListField(fields.StringField(), validate=[_validate_filters])
|
||||
|
||||
|
||||
@CTC2_INST.register
|
||||
class Guess(Document):
|
||||
"""Guess database object."""
|
||||
|
@ -82,7 +88,7 @@ class Lock(Document):
|
|||
guild: int = fields.IntegerField(required=True)
|
||||
reason: str = fields.StringField(required=True)
|
||||
original_perms: Permission = fields.EmbeddedField(Permission, required=False)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -95,7 +101,7 @@ class Lockdown(Document):
|
|||
guild: int = fields.IntegerField(required=True)
|
||||
reason: str = fields.StringField(required=True)
|
||||
original_perms: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -106,7 +112,7 @@ class Purge(Document):
|
|||
channel: int = fields.IntegerField(required=True)
|
||||
guild: int = fields.IntegerField(required=True)
|
||||
count: int = fields.IntegerField(default=10)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -118,8 +124,8 @@ class Reminder(Document):
|
|||
guild: int = fields.IntegerField(required=True)
|
||||
channel: int = fields.IntegerField(required=True)
|
||||
message: str = fields.StringField(required=True)
|
||||
remind_at: datetime = fields.DateTimeField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
remind_at: datetime = fields.AwareDateTimeField(required=True)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
private: bool = fields.BooleanField(default=False)
|
||||
|
||||
|
||||
|
@ -148,7 +154,7 @@ class Roleping(Document):
|
|||
guild: int = fields.IntegerField(required=True)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
bypass: Bypass = fields.EmbeddedField(Bypass)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -172,7 +178,7 @@ class Star(Document):
|
|||
guild: int = fields.IntegerField(required=True)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
star: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -182,7 +188,7 @@ class Starboard(Document):
|
|||
channel: int = fields.IntegerField(required=True)
|
||||
guild: int = fields.IntegerField(required=True)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -190,9 +196,9 @@ class Tag(Document):
|
|||
"""Tag database object."""
|
||||
|
||||
creator: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
editor: int = fields.IntegerField()
|
||||
edited_at: datetime = fields.DateTimeField()
|
||||
edited_at: datetime = fields.AwareDateTimeField()
|
||||
name: str = fields.StringField(required=True)
|
||||
content: str = fields.StringField(required=True)
|
||||
guild: int = fields.IntegerField(required=True)
|
||||
|
@ -206,8 +212,8 @@ class Temprole(Document):
|
|||
user: int = fields.IntegerField(required=True)
|
||||
role: int = fields.IntegerField(required=True)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
expires_at: datetime = fields.DateTimeField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
expires_at: datetime = fields.AwareDateTimeField(required=True)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
|
|
@ -4,11 +4,7 @@ from datetime import datetime, timezone
|
|||
from umongo import Document, fields
|
||||
|
||||
from jarvis_core.db import JARVIS_INST
|
||||
|
||||
|
||||
def get_now() -> datetime:
|
||||
"""Get proper timestamp."""
|
||||
return datetime.now(tz=timezone.utc)
|
||||
from jarvis_core.db.utils import get_now
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -22,7 +18,7 @@ class Ban(Document):
|
|||
guild: int = fields.IntegerField(required=True)
|
||||
type: str = fields.StringField(default="perm")
|
||||
reason: str = fields.StringField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -33,7 +29,7 @@ class Kick(Document):
|
|||
guild: int = fields.IntegerField(required=True)
|
||||
reason: str = fields.StringField(required=True)
|
||||
user: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -46,7 +42,7 @@ class Mute(Document):
|
|||
duration: int = fields.IntegerField(default=10)
|
||||
guild: int = fields.IntegerField(required=True)
|
||||
reason: str = fields.StringField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -59,7 +55,7 @@ class Unban(Document):
|
|||
guild: int = fields.IntegerField(required=True)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
reason: str = fields.StringField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -72,5 +68,5 @@ class Warning(Document):
|
|||
guild: int = fields.IntegerField(required=True)
|
||||
duration: int = fields.IntegerField(default=24)
|
||||
reason: str = fields.StringField(required=True)
|
||||
expires_at: datetime = fields.DateTimeField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
expires_at: datetime = fields.AwareDateTimeField(required=True)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
|
122
jarvis_core/db/models/backups.py
Normal file
122
jarvis_core/db/models/backups.py
Normal file
|
@ -0,0 +1,122 @@
|
|||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from umongo import Document, EmbeddedDocument, fields
|
||||
|
||||
from jarvis_core import __version__
|
||||
from jarvis_core.db import JARVIS_INST
|
||||
from jarvis_core.db.fields import BinaryField
|
||||
from jarvis_core.db.utils import get_id, get_now
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class Image(Document):
|
||||
discord_id: int = fields.IntegerField(unique=True)
|
||||
image_data: List[bytes] = BinaryField()
|
||||
image_ext: str = fields.StringField()
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class PermissionOverwriteBackup(EmbeddedDocument):
|
||||
id: int = fields.IntegerField()
|
||||
type: int = fields.IntegerField()
|
||||
allow: int = fields.IntegerField()
|
||||
deny: int = fields.IntegerField()
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class WebhookBackup(EmbeddedDocument):
|
||||
id: int = fields.IntegerField()
|
||||
channel_id: int = fields.IntegerField()
|
||||
type: int = fields.IntegerField()
|
||||
avatar: Image = fields.ReferenceField(Image)
|
||||
name: str = fields.StringField()
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class ChannelBackup(EmbeddedDocument):
|
||||
id: int = fields.IntegerField()
|
||||
name: str = fields.StringField()
|
||||
type: int = fields.IntegerField()
|
||||
position: int = fields.IntegerField()
|
||||
topic: Optional[str] = fields.StringField(default=None)
|
||||
nsfw: bool = fields.BooleanField(default=False)
|
||||
rate_limit_per_user: int = fields.IntegerField(default=None)
|
||||
bitrate: Optional[int] = fields.IntegerField(default=None)
|
||||
user_limit: Optional[int] = fields.IntegerField(default=None)
|
||||
permission_overwrites: List[PermissionOverwriteBackup] = fields.ListField(
|
||||
fields.EmbeddedField(PermissionOverwriteBackup), factory=list
|
||||
)
|
||||
parent_id: Optional[int] = fields.IntegerField(default=None)
|
||||
rtc_region: Optional[str] = fields.StringField(default=None)
|
||||
video_quality_mode: Optional[int] = fields.IntegerField(default=None)
|
||||
default_auto_archive_duration: Optional[int] = fields.IntegerField(default=None)
|
||||
webhooks: List[WebhookBackup] = fields.ListField(
|
||||
fields.EmbeddedField(WebhookBackup), factory=list
|
||||
)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class RoleBackup(EmbeddedDocument):
|
||||
id: int = fields.IntegerField()
|
||||
name: str = fields.StringField()
|
||||
permissions: int = fields.IntegerField()
|
||||
color: str = fields.StringField()
|
||||
hoist: bool = fields.BooleanField()
|
||||
mentionable: bool = fields.BooleanField()
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class EmojiBackup(EmbeddedDocument):
|
||||
id: int = fields.IntegerField()
|
||||
name: str = fields.StringField()
|
||||
image: Image = fields.ReferenceField(Image)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class StickerBackup(EmbeddedDocument):
|
||||
id: int = fields.IntegerField()
|
||||
name: str = fields.StringField()
|
||||
format_type: int = fields.IntegerField()
|
||||
tags: str = fields.StringField()
|
||||
type: int = fields.IntegerField()
|
||||
image: Image = fields.ReferenceField(Image)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class GuildBackup(EmbeddedDocument):
|
||||
name: str = fields.StringField(required=True)
|
||||
description: str = fields.StringField(default=None)
|
||||
default_message_notifications: Optional[int] = fields.IntegerField(default=None)
|
||||
explicit_content_filter: Optional[int] = fields.IntegerField(default=None)
|
||||
afk_channel: Optional[int] = fields.IntegerField(default=None)
|
||||
afk_timeout: Optional[int] = fields.IntegerField(default=None)
|
||||
icon: Optional[Image] = fields.ReferenceField(Image, default=None)
|
||||
owner: int = fields.IntegerField(required=True)
|
||||
splash: Optional[Image] = fields.ReferenceField(Image, default=None)
|
||||
discovery_splash: Optional[Image] = fields.ReferenceField(Image, default=None)
|
||||
banner: Optional[Image] = fields.ReferenceField(Image, default=None)
|
||||
system_channel: Optional[int] = fields.IntegerField(default=None)
|
||||
system_channel_flags: Optional[int] = fields.IntegerField(default=None)
|
||||
rules_channel: Optional[int] = fields.IntegerField(default=None)
|
||||
public_updates_channel: Optional[int] = fields.IntegerField(default=None)
|
||||
preferred_locale: Optional[str] = fields.StringField(default=None)
|
||||
features: List[str] = fields.ListField(fields.StringField, factory=list)
|
||||
channels: List[ChannelBackup] = fields.ListField(
|
||||
fields.EmbeddedField(ChannelBackup), factory=list
|
||||
)
|
||||
roles: List[RoleBackup] = fields.ListField(fields.EmbeddedField(RoleBackup), factory=list)
|
||||
emojis: List[EmojiBackup] = fields.ListField(fields.EmbeddedField(EmojiBackup), factory=list)
|
||||
stickers: List[StickerBackup] = fields.ListField(
|
||||
fields.EmbeddedField(StickerBackup), factory=list
|
||||
)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class Backup(Document):
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
guild_id: int = fields.IntegerField()
|
||||
bkid: str = fields.StringField(default=get_id)
|
||||
guild: GuildBackup = fields.EmbeddedField(GuildBackup, required=True)
|
||||
version: str = fields.StringField(default=__version__)
|
|
@ -2,24 +2,11 @@
|
|||
from datetime import datetime, timezone
|
||||
from typing import List
|
||||
|
||||
import nanoid
|
||||
from bson import ObjectId
|
||||
from umongo import Document, EmbeddedDocument, fields
|
||||
|
||||
from jarvis_core.db import JARVIS_INST
|
||||
|
||||
NANOID_ALPHA = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
|
||||
def get_now() -> datetime:
|
||||
"""Get proper timestamp."""
|
||||
return datetime.now(tz=timezone.utc)
|
||||
|
||||
|
||||
def get_id() -> str:
|
||||
"""Get nanoid."""
|
||||
return nanoid.generate(NANOID_ALPHA, 12)
|
||||
|
||||
from jarvis_core.db.utils import get_id, get_now
|
||||
|
||||
@JARVIS_INST.register
|
||||
class Action(EmbeddedDocument):
|
||||
|
@ -36,7 +23,7 @@ class Note(EmbeddedDocument):
|
|||
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
content: str = fields.StrField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -49,5 +36,5 @@ class Modlog(Document):
|
|||
admin: int = fields.IntegerField(required=True)
|
||||
actions: List[Action] = fields.ListField(fields.EmbeddedField(Action), factory=list)
|
||||
open: bool = fields.BoolField(default=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
notes: List[Note] = fields.ListField(fields.EmbeddedField(Note), factory=list)
|
||||
|
|
|
@ -1,56 +1,52 @@
|
|||
"""Reddit databaes models."""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from umongo import Document, fields
|
||||
|
||||
from jarvis_core.db import JARVIS_INST
|
||||
|
||||
|
||||
def get_now() -> datetime:
|
||||
"""Get proper timestamp."""
|
||||
return datetime.now(tz=timezone.utc)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class Subreddit(Document):
|
||||
"""Subreddit object."""
|
||||
|
||||
display_name: str = fields.StringField(required=True)
|
||||
over18: bool = fields.BooleanField(default=False)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class SubredditFollow(Document):
|
||||
"""Subreddit Follow object."""
|
||||
|
||||
active: bool = fields.BooleanField(default=True)
|
||||
display_name: str = fields.StringField(required=True)
|
||||
channel: int = fields.IntegerField(required=True)
|
||||
guild: int = fields.IntegerField(required=True)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
|
||||
class Meta:
|
||||
collection_name = "subredditfollow"
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class Redditor(Document):
|
||||
"""Reddit User object."""
|
||||
|
||||
name: str = fields.StringField(required=True)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class RedditorFollow(Document):
|
||||
"""Reditor Follow object."""
|
||||
|
||||
active: bool = fields.BooleanField(default=True)
|
||||
name: str = fields.StringField(required=True)
|
||||
channel: int = fields.IntegerField(required=True)
|
||||
guild: int = fields.IntegerField(required=True)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
|
||||
class Meta:
|
||||
collection_name = "redditorfollow"
|
||||
"""Reddit databaes models."""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from umongo import Document, fields
|
||||
|
||||
from jarvis_core.db import JARVIS_INST
|
||||
from jarvis_core.db.utils import get_now
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class Subreddit(Document):
|
||||
"""Subreddit object."""
|
||||
|
||||
display_name: str = fields.StringField(required=True)
|
||||
over18: bool = fields.BooleanField(default=False)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class SubredditFollow(Document):
|
||||
"""Subreddit Follow object."""
|
||||
|
||||
active: bool = fields.BooleanField(default=True)
|
||||
display_name: str = fields.StringField(required=True)
|
||||
channel: int = fields.IntegerField(required=True)
|
||||
guild: int = fields.IntegerField(required=True)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
class Meta:
|
||||
collection_name = "subredditfollow"
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class Redditor(Document):
|
||||
"""Reddit User object."""
|
||||
|
||||
name: str = fields.StringField(required=True)
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
class RedditorFollow(Document):
|
||||
"""Reditor Follow object."""
|
||||
|
||||
active: bool = fields.BooleanField(default=True)
|
||||
name: str = fields.StringField(required=True)
|
||||
channel: int = fields.IntegerField(required=True)
|
||||
guild: int = fields.IntegerField(required=True)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
class Meta:
|
||||
collection_name = "redditorfollow"
|
||||
|
|
|
@ -4,11 +4,7 @@ from datetime import datetime, timezone
|
|||
from umongo import Document, fields
|
||||
|
||||
from jarvis_core.db import JARVIS_INST
|
||||
|
||||
|
||||
def get_now() -> datetime:
|
||||
"""Get proper timestamp."""
|
||||
return datetime.now(tz=timezone.utc)
|
||||
from jarvis_core.db.utils import get_now
|
||||
|
||||
|
||||
@JARVIS_INST.register
|
||||
|
@ -18,7 +14,7 @@ class TwitterAccount(Document):
|
|||
handle: str = fields.StringField(required=True)
|
||||
twitter_id: int = fields.IntegerField(required=True)
|
||||
last_tweet: int = fields.IntegerField(required=True)
|
||||
last_sync: datetime = fields.DateTimeField(default=get_now)
|
||||
last_sync: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
class Meta:
|
||||
collection_name = "twitteraccount"
|
||||
|
@ -34,7 +30,7 @@ class TwitterFollow(Document):
|
|||
guild: int = fields.IntegerField(required=True)
|
||||
retweets: bool = fields.BooleanField(default=True)
|
||||
admin: int = fields.IntegerField(required=True)
|
||||
created_at: datetime = fields.DateTimeField(default=get_now)
|
||||
created_at: datetime = fields.AwareDateTimeField(default=get_now)
|
||||
|
||||
class Meta:
|
||||
collection_name = "twitterfollow"
|
||||
|
|
15
jarvis_core/db/utils.py
Normal file
15
jarvis_core/db/utils.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from datetime import datetime, timezone
|
||||
|
||||
import nanoid
|
||||
|
||||
NANOID_ALPHA = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
|
||||
def get_now() -> datetime:
|
||||
"""Get proper timestamp."""
|
||||
return datetime.now(tz=timezone.utc)
|
||||
|
||||
|
||||
def get_id() -> str:
|
||||
"""Get nanoid."""
|
||||
return nanoid.generate(NANOID_ALPHA, 12)
|
|
@ -1,206 +1,206 @@
|
|||
"""JARVIS quality of life utilities."""
|
||||
import hashlib
|
||||
from typing import Any, Callable, Iterable, List, Optional, Tuple, Union
|
||||
|
||||
from aiohttp import ClientSession
|
||||
|
||||
from jarvis_core.filters import url
|
||||
|
||||
DEFAULT_BLOCKSIZE = 8 * 1024 * 1024
|
||||
|
||||
|
||||
class Singleton(object):
|
||||
REQUIRED = []
|
||||
OPTIONAL = {}
|
||||
|
||||
def __new__(cls, *args: list, **kwargs: dict):
|
||||
"""Create a new singleton."""
|
||||
inst = cls.__dict__.get("inst")
|
||||
if inst is not None:
|
||||
return inst
|
||||
|
||||
inst = object.__new__(cls)
|
||||
inst.init(*args, **kwargs)
|
||||
inst._validate()
|
||||
cls.__inst__ = inst
|
||||
|
||||
return inst
|
||||
|
||||
def _validate(self) -> None:
|
||||
for key in self.REQUIRED:
|
||||
if not getattr(self, key, None):
|
||||
raise ValueError(f"Missing required key: {key}")
|
||||
|
||||
def init(self, **kwargs: dict) -> None:
|
||||
"""Initialize the object."""
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
for key, value in self.OPTIONAL.items():
|
||||
if not getattr(self, key, None):
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
async def hash(
|
||||
data: str, method: Union[Callable, str] = hashlib.sha256, size: int = DEFAULT_BLOCKSIZE
|
||||
) -> Tuple[str, int, str]:
|
||||
"""
|
||||
Hash an object.
|
||||
|
||||
Args:
|
||||
data: URL or text to hash
|
||||
method: Hash function/function name, default `hashlib.sha256`
|
||||
size: Blocksize, default `8 MB`
|
||||
|
||||
Returns:
|
||||
Tuple of hash hex, data size, and content type
|
||||
|
||||
Raises:
|
||||
Exception: Raise on status
|
||||
"""
|
||||
# Get hashlib method
|
||||
if isinstance(method, str):
|
||||
method = getattr(hashlib, method, hashlib.sha256)
|
||||
method = method()
|
||||
|
||||
# Hash non-URLs
|
||||
if not url.match(data):
|
||||
method.update(data.encode("UTF8"))
|
||||
return method.hexdigest(), len(data), "string"
|
||||
|
||||
# Hash URL contents
|
||||
async with ClientSession() as session:
|
||||
resp = await session.get(data)
|
||||
resp.raise_for_status()
|
||||
content_type = resp.content_type
|
||||
|
||||
# While we could use resp.content_length, that sometimes returns None,
|
||||
# so we manually count the size while looping.
|
||||
data_len = 0
|
||||
block_count = 0
|
||||
|
||||
async for block in resp.content.iter_chunked(n=size):
|
||||
data_len += len(block)
|
||||
block_count += 1
|
||||
method.update(block)
|
||||
|
||||
return method.hexdigest(), data_len, content_type
|
||||
|
||||
|
||||
def convert_bytesize(b: int) -> str:
|
||||
"""Convert bytes amount to human readable."""
|
||||
if not b:
|
||||
return "??? B"
|
||||
b = float(b)
|
||||
sizes = ["B", "KB", "MB", "GB", "TB", "PB"]
|
||||
size = 0
|
||||
while b >= 1024 and size < len(sizes) - 1:
|
||||
b = b / 1024
|
||||
size += 1
|
||||
return "{:0.3f} {}".format(b, sizes[size])
|
||||
|
||||
|
||||
def unconvert_bytesize(size: int, ending: str) -> int:
|
||||
"""Convert human readable to bytes."""
|
||||
ending = ending.upper()
|
||||
sizes = ["B", "KB", "MB", "GB", "TB", "PB"]
|
||||
if ending == "B":
|
||||
return size
|
||||
# Rounding is only because bytes cannot be partial
|
||||
return round(size * (1024 ** sizes.index(ending)))
|
||||
|
||||
|
||||
def find(predicate: Callable, sequence: Iterable) -> Optional[Any]:
|
||||
"""
|
||||
Find the first element in a sequence that matches the predicate.
|
||||
|
||||
??? Hint "Example Usage:"
|
||||
```python
|
||||
member = find(lambda m: m.name == "UserName", guild.members)
|
||||
```
|
||||
Args:
|
||||
predicate: A callable that returns a boolean value
|
||||
sequence: A sequence to be searched
|
||||
|
||||
Returns:
|
||||
A match if found, otherwise None
|
||||
|
||||
"""
|
||||
for el in sequence:
|
||||
if predicate(el):
|
||||
return el
|
||||
return None
|
||||
|
||||
|
||||
def find_all(predicate: Callable, sequence: Iterable) -> List[Any]:
|
||||
"""
|
||||
Find all elements in a sequence that match the predicate.
|
||||
|
||||
??? Hint "Example Usage:"
|
||||
```python
|
||||
members = find_all(lambda m: m.name == "UserName", guild.members)
|
||||
```
|
||||
Args:
|
||||
predicate: A callable that returns a boolean value
|
||||
sequence: A sequence to be searched
|
||||
|
||||
Returns:
|
||||
A list of matches
|
||||
|
||||
"""
|
||||
return [el for el in sequence if predicate(el)]
|
||||
|
||||
|
||||
def get(sequence: Iterable, **kwargs: Any) -> Optional[Any]:
|
||||
"""
|
||||
Find the first element in a sequence that matches all attrs.
|
||||
|
||||
??? Hint "Example Usage:"
|
||||
```python
|
||||
channel = get(guild.channels, nsfw=False, category="General")
|
||||
```
|
||||
|
||||
Args:
|
||||
sequence: A sequence to be searched
|
||||
kwargs: Keyword arguments to search the sequence for
|
||||
|
||||
Returns:
|
||||
A match if found, otherwise None
|
||||
"""
|
||||
if not kwargs:
|
||||
return sequence[0]
|
||||
|
||||
for el in sequence:
|
||||
if any(not hasattr(el, attr) for attr in kwargs.keys()):
|
||||
continue
|
||||
if all(getattr(el, attr) == value for attr, value in kwargs.items()):
|
||||
return el
|
||||
return None
|
||||
|
||||
|
||||
def get_all(sequence: Iterable, **kwargs: Any) -> List[Any]:
|
||||
"""
|
||||
Find all elements in a sequence that match all attrs.
|
||||
|
||||
??? Hint "Example Usage:"
|
||||
```python
|
||||
channels = get_all(guild.channels, nsfw=False, category="General")
|
||||
```
|
||||
|
||||
Args:
|
||||
sequence: A sequence to be searched
|
||||
kwargs: Keyword arguments to search the sequence for
|
||||
|
||||
Returns:
|
||||
A list of matches
|
||||
"""
|
||||
if not kwargs:
|
||||
return sequence
|
||||
|
||||
matches = []
|
||||
for el in sequence:
|
||||
if any(not hasattr(el, attr) for attr in kwargs.keys()):
|
||||
continue
|
||||
if all(getattr(el, attr) == value for attr, value in kwargs.items()):
|
||||
matches.append(el)
|
||||
return matches
|
||||
"""JARVIS quality of life utilities."""
|
||||
import hashlib
|
||||
from typing import Any, Callable, Iterable, List, Optional, Tuple, Union
|
||||
|
||||
from aiohttp import ClientSession
|
||||
|
||||
from jarvis_core.filters import url
|
||||
|
||||
DEFAULT_BLOCKSIZE = 8 * 1024 * 1024
|
||||
|
||||
|
||||
class Singleton(object):
|
||||
REQUIRED = []
|
||||
OPTIONAL = {}
|
||||
|
||||
def __new__(cls, *args: list, **kwargs: dict):
|
||||
"""Create a new singleton."""
|
||||
inst = cls.__dict__.get("inst")
|
||||
if inst is not None:
|
||||
return inst
|
||||
|
||||
inst = object.__new__(cls)
|
||||
inst.init(*args, **kwargs)
|
||||
inst._validate()
|
||||
cls.__inst__ = inst
|
||||
|
||||
return inst
|
||||
|
||||
def _validate(self) -> None:
|
||||
for key in self.REQUIRED:
|
||||
if not getattr(self, key, None):
|
||||
raise ValueError(f"Missing required key: {key}")
|
||||
|
||||
def init(self, **kwargs: dict) -> None:
|
||||
"""Initialize the object."""
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
for key, value in self.OPTIONAL.items():
|
||||
if not getattr(self, key, None):
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
async def hash(
|
||||
data: str, method: Union[Callable, str] = hashlib.sha256, size: int = DEFAULT_BLOCKSIZE
|
||||
) -> Tuple[str, int, str]:
|
||||
"""
|
||||
Hash an object.
|
||||
|
||||
Args:
|
||||
data: URL or text to hash
|
||||
method: Hash function/function name, default `hashlib.sha256`
|
||||
size: Blocksize, default `8 MB`
|
||||
|
||||
Returns:
|
||||
Tuple of hash hex, data size, and content type
|
||||
|
||||
Raises:
|
||||
Exception: Raise on status
|
||||
"""
|
||||
# Get hashlib method
|
||||
if isinstance(method, str):
|
||||
method = getattr(hashlib, method, hashlib.sha256)
|
||||
method = method()
|
||||
|
||||
# Hash non-URLs
|
||||
if not url.match(data):
|
||||
method.update(data.encode("UTF8"))
|
||||
return method.hexdigest(), len(data), "string"
|
||||
|
||||
# Hash URL contents
|
||||
async with ClientSession() as session:
|
||||
resp = await session.get(data)
|
||||
resp.raise_for_status()
|
||||
content_type = resp.content_type
|
||||
|
||||
# While we could use resp.content_length, that sometimes returns None,
|
||||
# so we manually count the size while looping.
|
||||
data_len = 0
|
||||
block_count = 0
|
||||
|
||||
async for block in resp.content.iter_chunked(n=size):
|
||||
data_len += len(block)
|
||||
block_count += 1
|
||||
method.update(block)
|
||||
|
||||
return method.hexdigest(), data_len, content_type
|
||||
|
||||
|
||||
def convert_bytesize(b: int) -> str:
|
||||
"""Convert bytes amount to human readable."""
|
||||
if not b:
|
||||
return "??? B"
|
||||
b = float(b)
|
||||
sizes = ["B", "KB", "MB", "GB", "TB", "PB"]
|
||||
size = 0
|
||||
while b >= 1024 and size < len(sizes) - 1:
|
||||
b = b / 1024
|
||||
size += 1
|
||||
return "{:0.3f} {}".format(b, sizes[size])
|
||||
|
||||
|
||||
def unconvert_bytesize(size: int, ending: str) -> int:
|
||||
"""Convert human readable to bytes."""
|
||||
ending = ending.upper()
|
||||
sizes = ["B", "KB", "MB", "GB", "TB", "PB"]
|
||||
if ending == "B":
|
||||
return size
|
||||
# Rounding is only because bytes cannot be partial
|
||||
return round(size * (1024 ** sizes.index(ending)))
|
||||
|
||||
|
||||
def find(predicate: Callable, sequence: Iterable) -> Optional[Any]:
|
||||
"""
|
||||
Find the first element in a sequence that matches the predicate.
|
||||
|
||||
??? Hint "Example Usage:"
|
||||
```python
|
||||
member = find(lambda m: m.name == "UserName", guild.members)
|
||||
```
|
||||
Args:
|
||||
predicate: A callable that returns a boolean value
|
||||
sequence: A sequence to be searched
|
||||
|
||||
Returns:
|
||||
A match if found, otherwise None
|
||||
|
||||
"""
|
||||
for el in sequence:
|
||||
if predicate(el):
|
||||
return el
|
||||
return None
|
||||
|
||||
|
||||
def find_all(predicate: Callable, sequence: Iterable) -> List[Any]:
|
||||
"""
|
||||
Find all elements in a sequence that match the predicate.
|
||||
|
||||
??? Hint "Example Usage:"
|
||||
```python
|
||||
members = find_all(lambda m: m.name == "UserName", guild.members)
|
||||
```
|
||||
Args:
|
||||
predicate: A callable that returns a boolean value
|
||||
sequence: A sequence to be searched
|
||||
|
||||
Returns:
|
||||
A list of matches
|
||||
|
||||
"""
|
||||
return [el for el in sequence if predicate(el)]
|
||||
|
||||
|
||||
def get(sequence: Iterable, **kwargs: Any) -> Optional[Any]:
|
||||
"""
|
||||
Find the first element in a sequence that matches all attrs.
|
||||
|
||||
??? Hint "Example Usage:"
|
||||
```python
|
||||
channel = get(guild.channels, nsfw=False, category="General")
|
||||
```
|
||||
|
||||
Args:
|
||||
sequence: A sequence to be searched
|
||||
kwargs: Keyword arguments to search the sequence for
|
||||
|
||||
Returns:
|
||||
A match if found, otherwise None
|
||||
"""
|
||||
if not kwargs:
|
||||
return sequence[0]
|
||||
|
||||
for el in sequence:
|
||||
if any(not hasattr(el, attr) for attr in kwargs.keys()):
|
||||
continue
|
||||
if all(getattr(el, attr) == value for attr, value in kwargs.items()):
|
||||
return el
|
||||
return None
|
||||
|
||||
|
||||
def get_all(sequence: Iterable, **kwargs: Any) -> List[Any]:
|
||||
"""
|
||||
Find all elements in a sequence that match all attrs.
|
||||
|
||||
??? Hint "Example Usage:"
|
||||
```python
|
||||
channels = get_all(guild.channels, nsfw=False, category="General")
|
||||
```
|
||||
|
||||
Args:
|
||||
sequence: A sequence to be searched
|
||||
kwargs: Keyword arguments to search the sequence for
|
||||
|
||||
Returns:
|
||||
A list of matches
|
||||
"""
|
||||
if not kwargs:
|
||||
return sequence
|
||||
|
||||
matches = []
|
||||
for el in sequence:
|
||||
if any(not hasattr(el, attr) for attr in kwargs.keys()):
|
||||
continue
|
||||
if all(getattr(el, attr) == value for attr, value in kwargs.items()):
|
||||
matches.append(el)
|
||||
return matches
|
||||
|
|
2
poetry.lock
generated
2
poetry.lock
generated
|
@ -326,7 +326,7 @@ multidict = ">=4.0"
|
|||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.10"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "4581dae71400051e27a4fedf8d3f91303102ab0e25e4b4392fa047f865419f3a"
|
||||
|
||||
[metadata.files]
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
[tool.poetry]
|
||||
name = "jarvis-core"
|
||||
version = "0.12.0"
|
||||
description = "JARVIS core"
|
||||
authors = ["Zevaryx <zevaryx@gmail.com>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
orjson = "^3.6.6"
|
||||
motor = "^2.5.1"
|
||||
umongo = "^3.1.0"
|
||||
PyYAML = "^6.0"
|
||||
pytz = "^2022.1"
|
||||
aiohttp = "^3.8.1"
|
||||
rich = "^12.3.0"
|
||||
nanoid = "^2.0.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^7.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
[tool.poetry]
|
||||
name = "jarvis-core"
|
||||
version = "0.13.0"
|
||||
description = "JARVIS core"
|
||||
authors = ["Zevaryx <zevaryx@gmail.com>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
orjson = "^3.6.6"
|
||||
motor = "^2.5.1"
|
||||
umongo = "^3.1.0"
|
||||
PyYAML = "^6.0"
|
||||
pytz = "^2022.1"
|
||||
aiohttp = "^3.8.1"
|
||||
rich = "^12.3.0"
|
||||
nanoid = "^2.0.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^7.1"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
Loading…
Add table
Reference in a new issue