Skip to content

Commit

Permalink
Merge pull request #599 from davidhozic/develop
Browse files Browse the repository at this point in the history
Merge develop before v4.1.x release
  • Loading branch information
davidhozic committed Sep 12, 2024
2 parents 450100c + 65f5448 commit cd2231a
Show file tree
Hide file tree
Showing 37 changed files with 1,361 additions and 856 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_exe.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
echo "from daf_gui import run;run()" > discord-advert-framework.py
pyinstaller --onefile --windowed discord-advert-framework.py --add-data src/daf_gui/img/:daf_gui/img/ --icon src/daf_gui/img/logo.png --add-data src/_discord/bin/:_discord/bin/
- name: Upload
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
files: |
dist/discord-advert-framework.exe
2 changes: 1 addition & 1 deletion .github/workflows/create_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
uses: actions/checkout@master
- name: Create tag
id: tag_version
uses: mathieudutour/github-tag-action@v6.1
uses: mathieudutour/github-tag-action@v6.2
with:
github_token: ${{ secrets.TOKEN_GH }}
custom_tag : ${{github.event.inputs.version-major}}.${{github.event.inputs.version-minor}}.${{github.event.inputs.version-bugfix}}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@2f6f737ca5f74c637829c0f5c3acd0e29ea5e8bf
uses: pypa/gh-action-pypi-publish@0ab0b79471669eb3a4d647e625009c62f9f3b241
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
17 changes: 17 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ Glossary
Releases
---------------------

v4.1.0
=====================
- New message period types:

+ :class:`daf.message.messageperiod.NamedDayOfYearPeriod`
+ :class:`daf.message.messageperiod.NamedDayOfMonthPeriod`

- Added optional case-sensitive matching to :class:`daf.logic.contains` and :class:`daf.logic.regex`. The default
is still case-insensitive.
- Added ``constraints`` parameter to :class:`daf.message.TextMESSAGE`. See :ref:`Message constraints` for more
information.
- Fixed SQL log removal through the GUI.
- Fixed CSV and JSON reading through remote.
- Disabled the :ref:`Automatic guild discovery and join` features due to the search provider shutting down its
services. It will be reenabled in a future version.


v4.0.5
=====================
- Fixed exe build.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 17 additions & 6 deletions docs/source/guide/core/shilldefine.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ We will not cover :class:`daf.guild.USER` separately as the definition process i
the same.
We will also not cover :class:`daf.guild.AutoGUILD` here, as it is covered in :ref:`Automatic Generation (core)`.

Let's define our :class:`daf.guild.GUILD` object now. Its most important parameters are:
Let's define our :class:`daf.guild.GUILD` object now. Its most important (but not all) parameters are:

- ``snowflake``: An integer parameter. Represents a unique identifier, which identifies every Discord resource.
Snowflake can be obtained by
Expand Down Expand Up @@ -146,6 +146,8 @@ Let's expand our example from :ref:`Definition of accounts (core)`.
daf.run(accounts=accounts)
Now let's define our messages.

--------------------------------------
Expand Down Expand Up @@ -178,13 +180,20 @@ The most important parameters inside :class:`daf.message.TextMESSAGE` are:
multiple specified days at a specific time.
- :class:`~daf.message.messageperiod.DailyPeriod`: A period that sends every day at a specific time.

- ``constraints``: A list of constraints that only allow a message to be sent when they are fulfilled. This can for
example be used to only send messages to channels when the last message in that channel is not or own, thus
**preventing unnecessary spam**. Currently a single constraint is supported:

- :class:`daf.message.constraints.AntiSpamMessageConstraint`

Now that we have an overview of the most important parameters, let's define our message.
We will define a message that sends fixed data into a single channel, with a fixed time (duration) period.

.. code-block:: python
:linenos:
:emphasize-lines: 1-3, 19-23
:emphasize-lines: 1-4, 19-24
from daf.message.constraints import AntiSpamMessageConstraint
from daf.message.messageperiod import FixedDurationPeriod
from daf.messagedata import TextMessageData
from daf.message import TextMESSAGE
Expand All @@ -201,12 +210,13 @@ We will define a message that sends fixed data into a single channel, with a fix
is_user=False, # Above token is user account's token and not a bot token.
servers=[
GUILD(
snowflake=863071397207212052,
snowflake=2312312312312312313123,
messages=[
TextMESSAGE(
data=TextMessageData(content="Looking for NFT?"),
channels=[1159224699830677685],
period=FixedDurationPeriod(duration=timedelta(seconds=15))
channels=[3215125123123123123123],
period=FixedDurationPeriod(duration=timedelta(seconds=5)),
constraints=[AntiSpamMessageConstraint(per_channel=True)]
)
]
)
Expand All @@ -217,6 +227,7 @@ We will define a message that sends fixed data into a single channel, with a fix
daf.run(accounts=accounts)
.. image:: ./images/message_definition_example_output.png
:width: 20cm

Expand All @@ -233,7 +244,7 @@ Additionally, it contains a ``volume`` parameter.
Message advertisement examples
--------------------------------------

The following examples show a complete core script setup needed to advertise periodic messages.
The following examples show a complete core script (without message constraints) setup needed to advertise periodic messages.

.. dropdown:: TextMESSAGE

Expand Down
6 changes: 6 additions & 0 deletions docs/source/guide/core/web.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ If you restart DAF, it will not re-login, but will just load the data from the s

Automatic guild discovery and join
======================================

.. error::

This feature is currently **disabled** due to the search provider shutting down its
services. It will be reenabled in a future version.

The web layer beside login with username and password, also allows (semi) automatic guild discovery and join.

To use this feature, users need to create an :class:`~daf.guild.AutoGUILD` instance, where they pass the ``auto_join``
Expand Down
10 changes: 5 additions & 5 deletions requirements/docs.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
sphinx==7.1.2
sphinx-autobuild==2021.3.14
sphinx==7.3.7
sphinx-autobuild==2024.9.3
sphinx-copybutton==0.5.2
furo==2024.1.29
enum-tools[sphinx]==0.11.0
sphinx-design[furo]==0.5.0
furo==2024.8.6
enum-tools[sphinx]==0.12.0
sphinx-design[furo]==0.6.1
readthedocs-sphinx-search==0.3.2
sphinxcontrib-svg2pdfconverter==1.2.2
6 changes: 3 additions & 3 deletions requirements/mandatory.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
aiohttp>=3.9.0,<3.10.0
aiohttp_socks>=0.8,<0.9
aiohttp>=3.9.0,<3.11.0
aiohttp_socks>=0.8,<0.10
typeguard>=2.13,<2.14
typing_extensions>=4,<5; python_version < "3.11"
tkinter-async-execute>=1.2,<1.3
tkinter-async-execute>=1.2,<1.4
asyncio-event-hub>=1.0,<1.2
tkclasswiz>=1.4,<1.5
ttkbootstrap==1.10.1
4 changes: 2 additions & 2 deletions requirements/sql.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
sqlalchemy[asyncio]>=2.0,<3.0
aiosqlite>=0.19,<0.20
pymssql>=2.2,<2.3
aiosqlite>=0.19,<0.21
pymssql>=2.2,<2.4
asyncpg>=0.29,<0.30
asyncmy>=0.2,<0.3
2 changes: 1 addition & 1 deletion requirements/testing.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pytest>=7.4,<8.1
pytest>=7.4,<8.4
pytest-asyncio>=0.21,<0.22
2 changes: 1 addition & 1 deletion requirements/web.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
selenium>=4.16,<4.18
selenium>=4.16,<4.25
undetected-chromedriver>=3.5,<3.6
webdriver-manager>=4.0,<4.1
2 changes: 1 addition & 1 deletion src/daf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import warnings


VERSION = "4.0.5"
VERSION = "4.1.0"


if sys.version_info.minor == 12 and sys.version_info.major == 3:
Expand Down
4 changes: 2 additions & 2 deletions src/daf/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ def import_class(path: str):
"attrs": ["_daf_id"]
},
logging.LoggerJSON: {
"attrs": []
"attrs": ["_daf_id"]
},
logging.LoggerCSV: {
"attrs": []
"attrs": ["_daf_id"]
},
discord.Embed: {
"custom_encoder": lambda embed: embed.to_dict(),
Expand Down
22 changes: 17 additions & 5 deletions src/daf/guild/autoguild.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ class AutoGUILD:
auto_join: Optional[web.GuildDISCOVERY] = None
.. versionadded:: v2.5
.. warning::
This is temporarily disabled until a new guild provider is found.
It will be reenabled in a future version.
Optional :class:`~daf.web.GuildDISCOVERY` object which will automatically discover
and join guilds though the browser.
This will open a Google Chrome session.
Expand Down Expand Up @@ -155,6 +159,14 @@ def __init__(
self._remove_after = remove_after
self._messages: List[MessageDuplicator] = []
self.logging = logging

if auto_join is not None: # TODO: remove in future after feature is reenabled.
auto_join = None
trace(
"Automatic join feature is currently disabled and will not work. It will be reenabled in a future version.",
TraceLEVELS.WARNING
)

self.auto_join = auto_join
self.parent = None
self.guild_query_iter = None
Expand Down Expand Up @@ -198,7 +210,7 @@ def _get_guilds(self):
client: discord.Client = self.parent.client
guilds = [
g for g in client.guilds
if self.include_pattern.check(g.name.lower())
if self.include_pattern.check(g.name)
]
return guilds

Expand Down Expand Up @@ -341,12 +353,12 @@ async def initialize(self, parent: Any, event_ctrl: EventController):
self._event_ctrl.add_listener(
EventID.discord_member_join,
self._on_member_join,
predicate=lambda memb: self.include_pattern.check(memb.guild.name.lower())
predicate=lambda memb: self.include_pattern.check(memb.guild.name)
)
self._event_ctrl.add_listener(
EventID.discord_invite_delete,
self._on_invite_delete,
predicate=lambda inv: self.include_pattern.check(inv.guild.name.lower())
predicate=lambda inv: self.include_pattern.check(inv.guild.name)
)
except discord.HTTPException as exc:
trace(f"Could not query invite links in {self}", TraceLEVELS.ERROR, exc)
Expand All @@ -358,7 +370,7 @@ async def initialize(self, parent: Any, event_ctrl: EventController):
self._event_ctrl.add_listener(
EventID.discord_guild_join,
self._on_guild_join,
predicate=lambda guild: self.include_pattern.check(guild.name.lower())
predicate=lambda guild: self.include_pattern.check(guild.name)
)

self._event_ctrl.add_listener(
Expand Down Expand Up @@ -471,7 +483,7 @@ async def get_next_guild():
try:
# Get next result from top.gg
yielded: web.QueryResult = await self.guild_query_iter.__anext__()
if self.include_pattern.check(yielded.name.lower()):
if self.include_pattern.check(yielded.name):
return yielded
except StopAsyncIteration:
trace(f"Iterated though all found guilds -> stopping guild join in {self}.", TraceLEVELS.NORMAL)
Expand Down
5 changes: 2 additions & 3 deletions src/daf/logging/sql/mgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1250,7 +1250,7 @@ async def __analytic_get_log(
)
return list(*zip(*logs.unique().all()))

async def delete_logs(self, logs: List[Union[MessageLOG, InviteLOG]]):
async def delete_logs(self, table: Union[MessageLOG, InviteLOG], primary_keys: List[int]):
"""
Method used to delete log objects objects.
Expand All @@ -1262,9 +1262,8 @@ async def delete_logs(self, logs: List[Union[MessageLOG, InviteLOG]]):
List of Primary Key IDs that match the rows of the table to delete.
"""
session: Union[AsyncSession, Session]
table = type(logs[0])
async with self.session_maker() as session:
await self._run_async(session.execute, delete(table).where(table.id.in_([log.id for log in logs])))
await self._run_async(session.execute, delete(table).where(table.id.in_(primary_keys)))
await self._run_async(session.commit)

@async_util.with_semaphore("_mutex")
Expand Down
34 changes: 14 additions & 20 deletions src/daf/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __init__(
*args,
operands: List[BaseLogic] = [],
) -> None:
self.operands = [*operands, *args]
self.operands: List[BaseLogic] = [*operands, *args]


@doc_category("Text matching (logic)")
Expand All @@ -60,12 +60,7 @@ class and_(BooleanLogic):

def check(self, input: str):
for op in self.operands:
if isinstance(op, BaseLogic):
check = op.check(input)
else:
check = op in input

if not check:
if not op.check(input):
return False

return True
Expand All @@ -83,12 +78,7 @@ class or_(BooleanLogic):
"""
def check(self, input: str):
for op in self.operands:
if isinstance(op, BaseLogic):
check = op.check(input)
else:
check = op in input

if check:
if op.check(input):
return True

return False
Expand All @@ -113,11 +103,8 @@ def operand(self):
return self.operands[0]

def check(self, input: str):
op = self.operands[0]
if isinstance(op, BaseLogic):
return not op.check(input)
return not self.operand.check(input)

return op not in input


@doc_category("Text matching (logic)")
Expand All @@ -130,10 +117,17 @@ class contains(BaseLogic):
keyword: str
The keyword needed to be inside a text message.
"""
def __init__(self, keyword: str) -> None:
self.keyword = keyword.lower()
def __init__(self, keyword: str, case_sensitive: bool = False) -> None:
self.case_sensitive = case_sensitive
if not case_sensitive:
keyword = keyword.lower()

self.keyword = keyword

def check(self, input: str):
if not self.case_sensitive:
input = input.lower()

return self.keyword in re.findall(r'\w+', input) # \w+ == match all words, including **bold**


Expand All @@ -157,7 +151,7 @@ class regex(BaseLogic):
def __init__(
self,
pattern: str,
flags: re.RegexFlag = re.MULTILINE,
flags: re.RegexFlag = re.MULTILINE | re.IGNORECASE,
full_match: bool = False
) -> None:
self._full_match = full_match
Expand Down
1 change: 1 addition & 0 deletions src/daf/message/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .text_based import *
from .messageperiod import *
from .autochannel import *
from .constraints import *

try:
from .voice_based import *
Expand Down
Loading

0 comments on commit cd2231a

Please sign in to comment.