Automatic responder

Added in version 4.0.0.

Discord Advertisement Framework also supports the so called automatic responder.

The automated responder can promptly reply to messages directed to either the guild or private messages. It can be customized to trigger based on specific keyword patterns within a message, provided the constrains are met.

Warning

For the automatic responder to work, ACCOUNT’s intents parameter needs to have the following intents enabled:

  • members

  • guild_messages

  • dm_messages

  • message_content

intents = Intents.default()
intents.members=True
intents.guild_messages=True
intents.dm_messages=True
intents.message_content=True

There are 2 responder classes:

They can be used on each account through the ACCOUNT’s responders parameter. The parameter is a list of responders.

1import daf
2
3daf.client.ACCOUNT(
4  ...,
5  responders=[daf.responder.DMResponder(...), ...]
6)

Both responders accept the following parameters:

condition:

Represents the message match condition. If both the condition and the constraints are met, then an action (response) will be triggered.

The Matching logic chapter explains how matching is done (same as daf.guild.AutoGUILD and daf.message.AutoCHANNEL)

action:

Represent the action taken upon message match (and constraint fulfillment). Currently the only action is a message response. There are 2 types of responses, which both send a message in response to the original trigger message.

Will send a reply to the message author’s private messages.

Will send a reply to same channel as the trigger message.

This is only available for the GuildResponder responder.

Both response classes accept a single parameter data of base type BaseTextData, which represents the data that will be sent in the response message. The BaseTextData type has two different implementations:

  • TextMessageData - for fixed data

    import daf
    
    daf.responder.DMResponse(
        data=daf.messagedata.TextMessageData("My content")
    )
    
  • DynamicMessageData - for data obtained through a function.

    import daf
    import requests
    
    class MyCustomText(DynamicMessageData):
        def __init__(self, a: int):
            self.a = a
    
        def get_data(self):  # Can also be async
            mydata = requests.get('https://daf.davidhozic.com').text
            return TextMessageData(content=mydata)
    
    daf.responder.DMResponse(
        data=MyCustomText(5)
    )
    
constraints:

In addition to the condition parameter, constrains represent extra checks. These checks (constraints) must all be fulfilled, otherwise no reply will me made to the trigger message.

A constraint can be any class implementing a constraint interface. The constraint interface is different for different responder types:

Constraints are implementations of the BaseDMConstraint interface.

Built-in implementations:

Constraints are implementations of the BaseGuildConstraint interface.

Built-in implementations:

In addition to the built-in implementations, custom constrains can be made by implementing one of the two interfaces.

Listing 12 Custom constraint implementation
from daf import discord
from daf.responder import GuildResponder
from daf.responder.constraints import BaseGuildConstraint

class MyConstraint(BaseGuildConstraint):
    def __init__(self, <some parameters>): ...

    def check(self, message: discord.Message, client: discord.Client) -> bool:
      return <True / False>

GuildResponder(..., constraints=[MyConstraint(...)])

Here is a full example of a DM responder:

../../_images/auto_dm_responder_example.png
 1
 2"""
 3The example shows how to use the automatic responder feature.
 4
 5DMResponder is used. It listens to messages inside DMs.
 6RegEx (regex) is used to match the text pattern.
 7MemberOfGuildConstraint is used for ensuring the author of the dm message is a member
 8of guild with ID 863071397207212052.
 9"""
10# Import the necessary items
11from _discord.flags import Intents
12from daf.messagedata.textdata import TextMessageData
13from daf.responder.constraints.dmconstraint import MemberOfGuildConstraint
14from daf.client import ACCOUNT
15from daf.responder.dmresponder import DMResponder
16from daf.responder.actions.response import DMResponse
17from daf.logic import regex
18import daf
19
20
21# Defined accounts
22intents = Intents.default()
23intents.members=True
24intents.guild_messages=True
25intents.dm_messages=True
26intents.message_content=True
27
28accounts = [
29    ACCOUNT(
30        token="CLIENT_TOKEN_HERE",
31        is_user=False,
32        intents=intents,
33        responders=[
34            DMResponder(
35                condition=regex(
36                    pattern="(buy|sell).*nft",
37                ),
38                action=DMResponse(
39                    data=TextMessageData(
40                        content="Instructions on how to buy / sell NFTs are provided on our official website:\nhttps://daf.davidhozic.com",
41                    ),
42                ),
43                constraints=[
44                    MemberOfGuildConstraint(
45                        guild=863071397207212052,
46                    ),
47                ],
48            ),
49        ],
50    ),
51]
52
53# Run the framework (blocking)
54daf.run(
55    accounts=accounts
56)

In the above example, we can see that the daf.responder.DMResponder has a regex condition defined. The RegEx pattern is as follows: (buy|sell).*nft. We can interpret the pattern as “match the message when it contains either the word “buy” or “sell”, followed by any text as long as “nft” also appears (after “buy” / “sell” ) in that text. We can also see a MemberOfGuildConstraint instance. It is given a guild=863071397207212052 ID parameter, which only allows a response if the author of the DM message is a member of the guild (server) with ID 863071397207212052.

The intents parameter of our ACCOUNT defines 4 intents. Intents are settings of which events Discord will send to our client. Without them the auto responder does not work.