dankgrinder / dankgrinder

An advanced automation program for the Dank Memer Discord bot
GNU Affero General Public License v3.0
70 stars 128 forks source link

How does the app view responses to messages? #115

Closed ghost closed 2 years ago

ghost commented 2 years ago

Hi,

Since you have discontinued development on dankgrinder, and since the fork has also been discontinued, I tried to make my own version of dankgrinder. However, I have only figured out how to send messages with the discord api. How does dankgrinder view reponses to commands and interact with things like buttons? I have tried looking through the source code but I don't know Go, and the discord api docs are not that helpful. Any help is appreciated

From, didlly

V4NSH4J commented 2 years ago

Hey, maintainer of the fork here. Dankgrinder views the responses to the commands via a Websocket connection to the discord gateway which is a constant connection and receives messages live from discord (which if done with regular requests would be very slow and inefficient) On a very superficial level, How dankgrinder is structured, you can setup a new routing condition which gets messages live from the websocket and applies conditions on them and "routes" them to the method where they are solved. In the words of grind95 it's like a HTTP middleware or a filter. You should select the conditions so that the message can be identified uniquely from all the other messages Dank Memer sends. You can find all the conditions you can set here and also define your own! Once you've got the routing conditions figured out, you make define the method on the instance struct where you actually solve the minigame. In case of search, it is picking the location with the highest priority or in the case of high-low, it's figuring out which number to press. You can extract information from the message using regular expressions and then apply logic to it to solve. Now we have the message routed and we have our solution, I believe you understood how you can use the scheduler to send messages. When I defined code for pressing buttons introduced in Discord API v9, I added buttons the same way the messages were added, so you can use the scheduler and add the Action row, Button and the message to press the button. You can find here a range of methods to work with buttons and ofcourse you can also define your own if you want to work with more Action Rows or generalize it a bit more. Like in.returnButtonLabel() can help you get the button labels to use them in your logic for solving the minigame. I recommend picking any one minigame which still works with buttons from my fork and following it through from the routing to the solving to the interacting and also see how it is added to the config.

I don't know much python so I can't give you any direction there, but you can try imitating dankgrinder's structure there as I explained above. Also, the Discord API Docs is a good resource, it won't fetch you direct answers but it would give you a basic idea about some things.

ghost commented 2 years ago

Thanks @V4NSH4J,

This will definitely help me. If you don't mind me asking another question, how do you make Discord think the user is typing?

V4NSH4J commented 2 years ago

Thanks @V4NSH4J,

This will definitely help me. If you don't mind me asking another question, how do you make Discord think the user is typing?

Hey, it's just a simple request for typing It's just for aesthetics, neither Discord nor Dank Memer (As far as I know) tracks it.

ghost commented 2 years ago

Thanks for your swift reply @V4NSH4J,

I have one more question (sorry lol). I have successfully setup code to monitor messages sent by Dank Memer. I am just having problems interacting with buttons. As I understand it, you need to send a request to https://discord.com/api/v9/interactions. However, I think the data I am sending is wrong. What data should be sent with this request? Below is the code I have so far:

data = {
    "message_id": latest_message["id"],
    "data": {
        "component_type": 2,
        "custom_id": button_id
    }
}

request = post(f"https://discord.com/api/v9/interactions", headers={"authorization": token}, data=data)
V4NSH4J commented 2 years ago
data := map[string]interface{}{"component_type": msg.Components[i].Buttons[k].Type, "custom_id": msg.Components[i].Buttons[k].CustomID, "hash": msg.Components[i].Buttons[k].Hash}
values := map[string]interface{}{"application_id": 270904126974590976, "channel_id": msg.ChannelID, "type": "3", "data": data, "guild_id": msg.GuildID, "message_flags": 0, "message_id": msg.ID, "nonce": client.snowflake()}

Here's the values variable is my payload from discord/discord.go The fields in data, meaning the custom_id and hash come from the message object which has the buttons. Application ID is the bot's (in this case DankMemer's) userid. The nonce is a Discord snowflake which is basically just a timestamp of the message. In my testing from back then, it didn't seem to matter if you sent it or not. The hash in data may be empty, if you message object's button's don't have a hash, you can omit it or send an empty one. Dankmemer initially had one so to represent a general case I kept it in.

ghost commented 2 years ago

Thanks,

One last question (sorry). How do you get the guild ID from the channel ID?

malbouy commented 2 years ago

Thanks,

One last question (sorry). How do you get the guild ID from the channel ID?

Hey! Not sure if this might be the answer you were looking for but in JS we get the guild details when fetching a channel, example we can simply do something like channel.guild.id to get the Guild ID from the channel ID.

let channel = await channels.fetch(channelID);
channel.guild.id

Hope this helped a bit with your query :))

ghost commented 2 years ago

Thanks, One last question (sorry). How do you get the guild ID from the channel ID?

Hey! Not sure if this might be the answer you were looking for but in JS we get the guild details when fetching a channel, example we can simply do something like channel.guild.id to get the Guild ID from the channel ID.

let channel = await channels.fetch(channelID);
channel.guild.id

Hope this helped a bit with your query :))

Hey @malbouy, thanks for trying to help. I think your answer describes how to get the channel ID using discord.js. I am trying to get the guild ID of a channel ID through the Discord api.

ghost commented 2 years ago

Thanks,

One last question (sorry). How do you get the guild ID from the channel ID?

Nevermind, I found it in the response from the Discord API when getting Dank Memer's message data.

ghost commented 2 years ago

@V4NSH4 I have managed to get it to work. I will soon push an update to add support for the pls search ,pls pm, pls highlow, pls crime & pls guess commands.

V4NSH4J commented 2 years ago

@V4NSH4 I have managed to get it to work. I will soon push an update to add support for the pls search ,pls pm, pls highlow, pls crime & pls guess commands.

Awesome! Let me know if you face any difficulties with the API or logic part

ghost commented 2 years ago

Hi @V4NSH4J,

I have another question. How do you get the account's name and tag from it's token?

V4NSH4J commented 2 years ago

Hi @V4NSH4J,

I have another question. How do you get the account's name and tag from it's token?

Hi! You can do a @me request to discord which would return you all the information linked with the token! It's a simple GET request to https://discord.com/api/v9/users/@me

ghost commented 2 years ago

Hey @V4NSH4J,

I am encountering a new error with the Discord API. Since today, whenever the self-bot tries to interact with a button, it receives the following error. Do you know any way to fix this? I cannot find a session_id in the json of Dank Memer's message.

b'{"code": 50035, "errors": {"session_id": {"_errors": [{"code": "BASE_TYPE_REQUIRED", "message": "This field is required"}]}}, "message": "Invalid Form Body"}

V4NSH4J commented 2 years ago

Hey @didlly Just checked it out, they indeed have changed something. My guess is they're doing this for their API V10 in which they're planning to make changes to how users interact with Discord Bots. They've added a field session_id which is to be sent in the payload for POST /api/v9/interactions/ The only session ID I was familiar with was for voice connections and I think it's used to reconnect to them. Moreover it seemed to be constant as long as you're on one device and don't refresh discord. Which made it pretty apparent that it's something that's coming from the Websocket READY event. So I connected to the gateway and printed out the Ready response and indeed it was there. It is being used to uniquely identify a discord session. KGYSQU1 Can also see my other sessions. I confirmed this is the right session ID by doing a request and checking the payload which was sent and it did match one of my active session IDs. This is a smart move by discord because till now selfbots could do requests without ever being connected to the gateway which regular users couldn't do. But now they will have to be connected to the websocket.

TL;DR / Fix -> 1) From the error, it seems like you can't send them an empty session ID. So try sending a random string like that. If it works, then you won't have to do step 2. If it doesn't work, Step 2 would surely work. 2) Whenever you connect to the gateway, cache the session ID from the ready event and use it for all your requests.

V4NSH4J commented 2 years ago

I've implemented 2 on my fork if you want to check it out. Getting the session ID from the ready event and using it in the requests!

ghost commented 2 years ago

Thanks a lot @V4NSH4J.

ghost commented 2 years ago

Hey @V4NSH4J,

Just saying, the Dank Memer devs have announced that in April they will re-write their bot to use slash commands entirely. I have worked out how to send slash commands through Discord's API, which you can see here.

ghost commented 2 years ago

Hey @V4NSH4J, I have a few questions for you if you don't mind.

1) How do you check if a channel id is a dm / group dm via the Discord API? 2) Is there a way to get the guild id of a channel id without trying to find msgs with replies since reply messages have the guild id?

V4NSH4J commented 2 years ago

Hey @didlly This is what you're looking for -> GET /api/v9/channels/1234 (Where 1234 is the Channel ID)

1) You can do this request, and in the response there would be a "type" field. Type 0: Regular Channel ID (Like that of a server) Type 1: Regular DM Channel ID Type 3: Group DM

You can GET api/v9/users/@me/channels with a payload like {"recipients":["1234"]} (where 1234 is the user ID) to get a DM's Channel ID

2) Doing the same request, there would be a "guild_id" field from where you can get the guild id for that channel (If it's a server's channel ofcourse)

Let me know if you have anything else you'd like to ask

ghost commented 2 years ago

Hey @didlly This is what you're looking for -> GET /api/v9/channels/1234 (Where 1234 is the Channel ID)

1) You can do this request, and in the response there would be a "type" field. Type 0: Regular Channel ID (Like that of a server) Type 1: Regular DM Channel ID Type 3: Group DM

You can GET api/v9/users/@me/channels with a payload like {"recipients":["1234"]} (where 1234 is the user ID) to get a DM's Channel ID

2) Doing the same request, there would be a "guild_id" field from where you can get the guild id for that channel (If it's a server's channel ofcourse)

Let me know if you have anything else you'd like to ask

Thank you!

ghost commented 2 years ago

Hi @V4NSH4J, If you wouldn't mind me asking, how do you generate the nonce that is sent on message send events? I have tried converting dankgrinder's code for it into Python but cannot get it to work.

V4NSH4J commented 2 years ago

Hi @V4NSH4J, If you wouldn't mind me asking, how do you generate the nonce that is sent on message send events? I have tried converting dankgrinder's code for it into Python but cannot get it to work.

Hey, the nonce is a discord snowflake like User IDs, server IDs and Channel IDs. It is a timestamp of the action. For example, user IDs when put throw the below web-chart would return the time of creation of account, same for channels and servers. It was fairly well documented although never called a nonce there I think so hard to find in the docs. image They provide this very informative web-chart which we just have to reverse to find the nonce. 1) Find the current unix timestamp 2) Convert it into milliseconds 3) Subtract Discord Epoch in milliseconds from it essentially getting the time since creation of discord in milliseconds 4) Convert to base 2 5) Add 22 zero's after it which are internal process IDs but 0's in case of nonces as they are only meant to be time-stamps and not carry anything else. 6) Convert to base 10

Few things to keep in mind:- 1) They aren't required to be sent, things work normally without them, although since the client sends it, you should too. 2) Always use it as a string as recommended, might cause integer overflows on 32 bit systems otherwise.

ghost commented 2 years ago

Thank you, I added the function here.