How do you have a Discord bot read DMs sent to it? (discord.py) - python-3.6

I've come up with a way to DM people, but I want to know what they say back to the bot through DMs, as if the bot "reads" the DM, and then forwards it to some channel in a discord server of mine, or, even better, DM it to me.
Here is my starting code:
if message.content.startswith("!dm"):
if message.author.id == "[YOUR ID HERE]":
memberID = "ID OF RECIPIENT"
server = message.server
person = discord.Server.get_member(server, memberID)
await client.delete_message(message)
await client.send_message(destination = person, content = "WHAT I'D LIKE TO SAY TO THEM")
I do it a different way contrary to how people do it with defining functions, I use a more basic way of making commands.
Any help is appreciated!

Here's a quick example. I've moved your existing command into an actual Command object, so the forwarding logic is the only thing in on_message
from discord.ext import commands
bot = commands.bot('!')
# I've moved the command out of on_message so it doesn't get cluttered
#bot.event
async def on_message(message):
channel = bot.get_channel('458778457539870742')
if message.server is None and message.author != bot.user:
await bot.send_message(channel, message.content)
await bot.process_commands(message)
# This always sends the same message to the same person. Is that what you want?
#bot.command(pass_context=True)
#commands.is_owner() # The account that owns the bot
async def dm(ctx):
memberID = "ID OF RECIPIENT"
person = await bot.get_user_info(memberID)
await bot.send_message(person, "WHAT I'D LIKE TO SAY TO THEM")
await bot.delete_message(ctx.message)

Related

is Firestore listen method effect performance app

my question is very simple but it mean a lot to me , i have more than 4 listen method in my flutter app which i can't use .cancle() to them since they are so important to keep in listen as long as user is active ,
Now my question is : let's say the app has thousands of active users monthly .. does it will effect of response performance of Firestore with these 4 listens methods for thousands of monthly active users ?
my listens methods are similar like this kind of data size .. most of them are moving data FROM/TO another collection's
FirebaseFirestore.instance.collection("users").doc(currentUser.uid).collection("handleCountM").where("new", isEqualTo: true).snapshots();
.listen((value){
value.docs.forEach((val) async {
String textMessage = await val.get("textMessage");
String image1 = await val.get("image1");
var timestamp = await val.get("timestamp");
String messageTime = await val.get("messageTime");
String name = await val.get("name");
String bio = await val.get("bio");
String senderUid = await val.get("senderUid");
String receiverUid = await val.get("receiverUid");
String notificationId = await val.get("notificationId");
final DocumentSnapshot countM2Id = await FirebaseFirestore.instance.collection("users").doc(currentUser.uid).collection("messages").doc(val.id).get();
if(countM2Id.exists ) {
int countM2 = await countM2Id.get("countM2");
final QuerySnapshot<Map<String, dynamic>> lengthId = await FirebaseFirestore.instance.collection("users").doc(currentUser.uid).collection("chats1").doc(val.id+currentUser.uid).collection("chats2").get();
await FirebaseFirestore.instance.collection("users").doc(currentUser.uid).collection("messages").doc(val.get("senderUid")).update({
"textMessage": textMessage,
"image1": image1,
"timestamp": timestamp,
"messageTime": messageTime,
"name": name,
"bio": bio,
"senderUid": senderUid,
"receiverUid": receiverUid,
"countM2": countM2,
"countM": lengthId.docs.length - countM2,
"notificationId": notificationId,
});
also i have read in firestore docs best performance section that should put under 100 listen snapshot methods fo each user . does it mean in my case no worries ? or they mean something is difference of my understanding .
thanks in advance
You're probably safe. It's really hard to have low performance on Firestore.
As far as I know, when it comes to performance and even costs the you only thing you need to worry is about how much data you're getting each time. It's easier to have performance issues with network (when you grab too much data in one time) so don't make your documents too big and don't grab too many documents at once that you may not need.
Also, setting up some listeners per user isn't a big deal either. Just be aware about how often you'll get charged for new reads when they are active if the data they're listening changes too often.
For instance, if the data on those collections are changing several times and you have thousands of users listening to the same collection queries, you may be charged for a lot of new reads every time there's a new value.

How to improve antispam function discord.py?

I'm making a discord bot and I have antispam function build in, but I want to improve it. I don't want this function to react to certain roles, like admin or moderator (so I want this function NOT to react to certain roles, so admins can spam). Also I have spam channel where I don't want this function to work, how do I do that? And last, but not least: how do I delete the same (spammed) messages? Because what my bot does is: it says "Stop spamming". But I want to delete the same message. Thank you very much! I hope you can help me!
THE CODE:
import os
import discord
import datetime
intents = discord.Intents.all()
bot = commands.Bot(command_prefix='$', intents=intents)
time_window_milliseconds = 5000
max_msg_per_window = 5
author_msg_times = {}
#bot.event
async def on_message(ctx): #spam allowed in channel and for admins? How to make?
global author_msg_counts
author_id = ctx.author.id
curr_time = datetime.datetime.now().timestamp() * 1000
if not author_msg_times.get(author_id, False):
author_msg_times[author_id] = []
author_msg_times[author_id].append(curr_time)
expr_time = curr_time - time_window_milliseconds
expired_msgs = [
msg_time for msg_time in author_msg_times[author_id]
if msg_time < expr_time
]
for msg_time in expired_msgs:
author_msg_times[author_id].remove(msg_time)
if len(author_msg_times[author_id]) > max_msg_per_window:
await ctx.delete(ctx)
await ctx.send("Stop Spamming")
else:
print("You are good to go!")
bot.run(os.getenv('token'))
For your first question about roles, you should add at the start of your spam check:
if ctx.author.guild_permissions.manage_messages:
return
This will make it not affect any member who has the permission to delete messages (this would be moderators or admins).
You can also do
if ctx.channel.id == [id here]:
return
replacing [id here] with the id of your spam channel.
Lastly, put await ctx.send("Stop Spamming") before await ctx.delete(ctx), and make sure your bot has permission to delete messages.

How to disconnect a discord bot from voice channel after user left?

I would like to add an event that my music bot leaves the voice channel immediately if the user leaves while the song is still playing. If there are multiple users in the channel, the bot should of course stay in the channel. I only have one approach there, but would need help. I would try the following:
async def check_member(self, ctx):
channel = ctx.author.voice.channel
member_count = len(voice_channel.members)
if member_count == 1:
await channel.disconnect
but somehow this doesn't seem to work.
I know for fact that there is a similar post but this did not work for me too as I defined some things different.
My second attempt was:
async def check_member(self, ctx):
channel = ctx.author.voice.channel
member_count = len(channel.members)
client = ctx.guild.voice_client
if member_count == 1:
await client.disconnect()
(Did not work either.)
To define does not work: I build the function in a different way now:
#tasks.loop(seconds=2)
async def check(self, ctx):
voice_channel = ctx.author.voice.channel
member_count = len(voice_channel.members)
client = ctx.guild.voice_client
if member_count == 1:
await client.disconnect()
This is now a total different count. What I am trying to do is loop the commands.Cog.listener() function every 2 seconds. For tests I played a song and left instantly after the bot started playing. I thought the bot would leave the channel too but it did not. There was no output in my log that I defined something wrong.
A loop is kinda unefficient, you can simply use the on_voice_state_update event
async def on_voice_state_update(member, before, after):
voice_state = member.guild.voice_client
# Checking if the bot is connected to a channel and if there is only 1 member connected to it (the bot itself)
if voice_state is not None and len(voice_state.channel.members) == 1:
# You should also check if the song is still playing
await voice_state.disconnect()
Reference:
on_voice_state_update

MS-Graph read tasks with admin consent

I am trying to read the Planner task for the users of my tenant. So I configured admin consent for "Tasks.Read", "Tasks.Read.Shared", "Groups.Read.All", "Groups.ReadWrite.All" for the app that is doing that.
Like mentioned here: https://learn.microsoft.com/de-de/graph/api/planneruser-list-tasks?view=graph-rest-1.0&tabs=http
I desined my code to get a token like mentioned here: https://briantjackett.com/2018/12/13/introduction-to-calling-microsoft-graph-from-a-c-net-core-application/
I get a token back and it is valid. (Checked with baerer token check tool.)
I thought that I could access the tasks from the Graph API like '/v1.0/users/{userId}/planner/tasks' but I get HTTP 401 back.
Can anyone give me the missing link? Thanks a lot.
_appId = configuration.GetValue<string>("AppId");
_tenantId = configuration.GetValue<string>("TenantId");
_clientSecret = configuration.GetValue<string>("ClientSecret");
_clientApplication = ConfidentialClientApplicationBuilder
.Create(_appId)
.WithTenantId(_tenantId)
.WithClientSecret(_clientSecret)
.Build();
var graphClient = GraphClientFactory.Create(new DelegateAuthenticationProvider(Authenticate));
var result = await graphClient.GetAsync($"/v1.0/users/{userId}/planner/tasks")
public async Task<string> GetTokenAsync()
{
AuthenticationResult authResult = await _clientApplication.AcquireTokenForClient(_scopes)
.ExecuteAsync();
return authResult.AccessToken;
}
private async Task Authenticate(HttpRequestMessage request)
{
var token = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
Reading tasks for other users is currently not allowed. A user can only read their assigned tasks. As an alternative, you can read the tasks in specific Plans, and sort out the users from that data, if you want to collect assignments from a set of Plans. You can provide feedback on this behavior in Planner UserVoice with the description of what you are trying to accomplish.
Additionally, application permissions are supported now, if that works for your scenario.
/v1.0/users/{userId}/planner/tasks is for getting tasks via getting a user, and you will need User permissions (User.Read.All) to get tasks via that call.
(Also Do you really need Groups.ReadWrite.All? Are you making changes to groups? -- it's not in your original description)

Akka non blocking options when an HTTP response is requied

I understand how to make a message based non-blocking application in akka, and can easily mock up examples that perform
concurrent operations and pass back the aggregated results in a message. Where I have difficulty is understanding what my
non-blocking options are when my application has to respond to an HTTP request. The goal is to receive a request and
immediately hand it over to a local or remote actor to do the work, which in turn will hand it off to get a result that
could take some time. Unfortunatly under this model, I don't understand how I could express this with a non-blocking
series of "tells" rather than blocking "asks". If at any point in the chain I use a tell, I no longer have a future to
use as the eventual response content (required by the http framework interface which in this case is finagle - but that is not
important). I understand the request is on its own thread, and my example is quite contrived, but just trying to
understand my design options.
In summary, If my contrived example below can be reworked to block less I very much love to understand how. This is my
first use of akka since some light exploration a year+ ago, and in every article, document, and talk I have viewed says
not to block for services.
Conceptual answers may be helpful but may also be the same as what I have already read. Working/Editing my example
would likely be key to my understanding of the exact problem I am attempting to solve. If the current example is generally
what needs to be done that confirmation is helpful too, so I don't search for magic that does not exist.
Note The following aliases: import com.twitter.util.{Future => TwitterFuture, Await => TwitterAwait}
object Server {
val system = ActorSystem("Example-System")
implicit val timeout = Timeout(1 seconds)
implicit def scalaFuture2twitterFuture[T](scFuture: Future[T]): TwitterFuture[T] = {
val promise = TwitterPromise[T]
scFuture onComplete {
case Success(result) ⇒ promise.setValue(result)
case Failure(failure) ⇒ promise.setException(failure)
}
promise
}
val service = new Service[HttpRequest, HttpResponse] {
def apply(req: HttpRequest): TwitterFuture[HttpResponse] = req.getUri match {
case "/a/b/c" =>
val w1 = system.actorOf(Props(new Worker1))
val r = w1 ? "take work"
val response: Future[HttpResponse] = r.mapTo[String].map { c =>
val resp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)
resp.setContent(ChannelBuffers.copiedBuffer(c, CharsetUtil.UTF_8))
resp
}
response
}
}
//val server = Http.serve(":8080", service); TwitterAwait.ready(server)
class Worker1 extends Actor with ActorLogging {
def receive = {
case "take work" =>
val w2 = context.actorOf(Props(new Worker2))
pipe (w2 ? "do work") to sender
}
}
class Worker2 extends Actor with ActorLogging {
def receive = {
case "do work" =>
//Long operation...
sender ! "The Work"
}
}
def main(args: Array[String]) {
val r = service.apply(
com.twitter.finagle.http.Request("/a/b/c")
)
println(TwitterAwait.result(r).getContent.toString(CharsetUtil.UTF_8)) // prints The Work
}
}
Thanks in advance for any guidance offered!
You can avoid sending a future as a message by using the pipe pattern—i.e., in Worker1 you'd write:
pipe(w2 ? "do work") to sender
Instead of:
sender ! (w2 ? "do work")
Now r will be a Future[String] instead of a Future[Future[String]].
Update: the pipe solution above is a general way to avoid having your actor respond with a future. As Viktor points out in a comment below, in this case you can take your Worker1 out of the loop entirely by telling Worker2 to respond directly to the actor that it (Worker1) got the message from:
w2.tell("do work", sender)
This won't be an option if Worker1 is responsible for operating on the response from Worker2 in some way (by using map on w2 ? "do work", combining multiple futures with flatMap or a for-comprehension, etc.), but if that's not necessary, this version is cleaner and more efficient.
That kills one Await.result. You can get rid of the other by writing something like the following:
val response: Future[HttpResponse] = r.mapTo[String].map { c =>
val resp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)
resp.setContent(ChannelBuffers.copiedBuffer(c, CharsetUtil.UTF_8))
resp
}
Now you just need to turn this Future into a TwitterFuture. I can't tell you off the top of my head exactly how to do this, but it should be fairly trivial, and definitely doesn't require blocking.
You definitely don't have to block at all here. First, update your import for the twitter stuff to:
import com.twitter.util.{Future => TwitterFuture, Await => TwitterAwait, Promise => TwitterPromise}
You will need the twitter Promise as that's the impl of Future you will return from the apply method. Then, follow what Travis Brown said in his answer so your actor is responding in such a way that you do not have nested futures. Once you do that, you should be able to change your apply method to something like this:
def apply(req: HttpRequest): TwitterFuture[HttpResponse] = req.getUri match {
case "/a/b/c" =>
val w1 = system.actorOf(Props(new Worker1))
val r = (w1 ? "take work").mapTo[String]
val prom = new TwitterPromise[HttpResponse]
r.map(toResponse) onComplete{
case Success(resp) => prom.setValue(resp)
case Failure(ex) => prom.setException(ex)
}
prom
}
def toResponse(c:String):HttpResponse = {
val resp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)
resp.setContent(ChannelBuffers.copiedBuffer(c, CharsetUtil.UTF_8))
resp
}
This probably needs a little more work. I didn't set it up in my IDE, so I can't guarantee you it compiles, but I believe the idea to be sound. What you return from the apply method is a TwitterFuture that is not yet completed. It will be completed when the future from the actor ask (?) is done and that's happing via a non-blocking onComplete callback.

Resources