I have made a telegram member scraper and inviter with Python. It was successful on some tests, but there are times my accounts get banned with upon seeing this error message:
AttributeError: 'ChatForbidden' object has no attribute 'access_hash'
I'm not sure why would it show ChatForbidden if I am already an admin of a group. It's hard to test these as I had to buy new phone numbers every time.
Here's a sample and explanation of my code to invite members to a group:
# Log in into the telegram account
client = TelegramClient('Tg_scraper', api_id, api_hash)
chats = []
last_date = None
chunk_size = 200
groups = []
hash_list = []
# Get all the groups/channel of the account
result = client(GetDialogsRequest(
offset_date=last_date,
offset_id=0,
offset_peer=InputPeerEmpty(),
limit=chunk_size,
hash=0
))
chats.extend(result.chats)
# Puts all the group/channel into a list
i = 0
print('Enter a NUMBER to choose a group where the members will be invited into:')
for chat in chats:
try:
groups.append(chat)
hash_list.append(chat.access_hash)
print(f"({i})" + ' - ' + chat.title)
i += 1
except:
continue
g_index = input("Enter a Number: ")
target_group = groups[int(g_index)]
target_group_entity = InputPeerChannel(target_group.id, target_group.access_hash)
Upon the last line, target_group_entity = InputPeerChannel(target_group.id, target_group.access_hash) is where I encounter the error I have stated above. Upon receiving that error, I get banned.
Does this have something to do with permissions? Do new accounts get banned for botting? It works on my first few tests, but then now I can't invite. Thank you so much for anyone who could help in advance.
I am already an admin of a group
This error is unrelated to your permission level.
Upon the last line is where I encounter the error
Wrong. you encounter this error because you're not coding it right with types in mind, expecting all your .chats are groups. Telegram doesn't tell you what fields exactly have, as you see in this error.
You must use type checking to limit your chats objects to only what you expect, your try block is appending then erroring, so, rather than a plain:
except:
continue
you need to actually confirm it won't error when accessing fields.
print('Enter a NUMBER to choose a group where the members will be invited into:')
i = 0
for chat in chats:
if isinstance(chat, telethon.types.Channel):
if chat.broadcast: continue # ignore non-group
groups.append(chat)
hash_list.append(chat.access_hash)
print(f"({i})" + ' - ' + chat.title)
i += 1
g_index = input("Enter a Number: ")
target_group = groups[int(g_index)]
Related
In telegram when I click Subscribers it shows me about 50 last users and about 150-200 deleted users.
I tried this:
async for user in client.iter_participants(chat_id):
if user.deleted:
print(user)
This gives me only last 50 users and 6-8 deleted users. I need all 150-200 deleted users. How can I get them?
I solved this problem using GetParticipantsRequest with offset parameter somehow like this:
from telethon.tl.functions.channels import GetParticipantsRequest
from telethon.tl.types import ChannelParticipantsSearch
chat_id = -123456
offset = 0
while True:
participants = await client(GetParticipantsRequest(
channel=chat_id,
filter=ChannelParticipantsSearch(''),
offset=offset,
limit=10000,
hash=0
))
deleted_users = []
for user in participants:
if user.deleted:
deleted_users.append(user)
if not deleted_users:
break
# doings with deleted_users
Not sure about iter_participants, but get_participants works in my case.
channel_id = -1234567890 # TODO: add channel id
users = client.get_participants(client.get_input_entity(channel_id))
for user in users:
if user.deleted:
print(user)
I have a development setup (Laptop, Django, Dialogflow, Ngrok) When I test my chatbot all is well.
I have a production setup (Ubuntu server hosted by Digital Ocean, Django, Dialogflow, Nginx, gunicorn) When I test dialog flow has troube matching intents. i.e i have to repeat the same sentence a few times until it matches the intent.
Any ideas why this might be?
I have tried pointing dialogflow fullfillment to my development environment and hit it from the production environment however i still have to repeat myself to get a matched intent.
I have tried pointing dialogflow to the production environment and hitting it from the development server and all works as it should.
conversation tested on production server:
Hello Chris from Cemlyn group! What can I do for you? Something map related perhaps?
return a map
Sorry, I dont understand what you want.
return a map
Sorry, I dont understand what you want.
return a map
Chris what map do you want to return?
1
Sorry, I dont understand what you want.
1
Sorry, I dont understand what you want.
1
OK just to confirm, you want me to return 1 for you Chris
return a map
Chris what map do you want to return?
1
Sorry, I dont understand what you want.
1
Sorry, I dont understand what you want.
1
OK just to confirm, you want me to return 1 for you Chris
Conversation tested from development setup:
Hello Chris from Cemlyn group! What can I do for you? Something map related perhaps?
return a map
Chris what map do you want to return?
1
OK just to confirm, you want me to return 1 for you Chris
the webhook looks like this:
def webhook(request):
# build a request object
req = json.loads(request.body)
#s = Session.objects.get(pk='')
#print(s)
# get action from json
action = req.get('queryResult').get('action')
params = req.get('queryResult').get('parameters')
print('session hitting webhook - '*3)
print(request.session.session_key)
for key, value in request.session.items():
print('{} => {}'.format(key, value))
print(req)
if action == 'issue_a_map':
if 'Maps' in params:
if params['Maps']:
mapid = int((params['Maps'][0]))
group = (params['ValleyGroups'][0])
if mapid == 99:
fulfillmentText = {'fulfillmentText': 'issue the next map for ' + group + ' from webhook.'}
else:
fulfillmentText = {'fulfillmentText': 'issue a map from webhook.'}
elif action == 'return_a_map':
print('return a map - '*4)
if 'Maps' in params:
if params['Maps']:
mapid = int(params['Maps'])
map = CongMap.objects.get(map_no=mapid)
if map.issued:
retuner = params['username']
return_map_form(request, mapid, retuner )
fulfillmentText = {'fulfillmentText': 'OK I will return map ' + str(mapid) + ' for you ' +params['username']}
else:
#reply = prepresponse('name please','admin','welcome_event')
fulfillmentText = {'fulfillmentText': 'Hmmm there is a problem, that map isnt issued currently. A map has to be issued before it can be returned.'}
ffr=fulfillment_response()
fftext= ffr.fulfillment_text(fulfillmentText)
params = {'problem': str(map.map_title) +' map hasnt been issued however we are trying to return it.'}
fue = ffr.followup_event_input('ProblemEvent', params)
ffm = None
ocx = None
reply = ffr.main_response(fftext, ffm, ocx, fue)
return JsonResponse(reply, safe=False)
else:
fulfillmentText = {'fulfillmentText': 'Sorry I need to know the map no or name.'}
else:
fulfillmentText = {'fulfillmentText': 'Sorry I need to know the map no or name.'}
elif action == 'help_problem':
fulfillmentText = {'fulfillmentText': 'OK ' +params['username'] +', I will send him an email asking him to help.'}
send_mail('Territory Problem', '{}. Please can you help {} with this problem'.format(params['problem'], params['username']), 'territorybot#chivers.io', ['tain259#gmail.com'])
else:
fulfillmentText = {'fulfillmentText': 'Sorry, I dont understand what you want.'}
# return response
return JsonResponse(fulfillmentText, safe=False)
Our company has a Customer Service (CS) process where after a client reports an error with our software, we'll get an email about their complain with generic subject ("User Submitted Error") and short description of the error. We then fix the problem and email back to client. An issue may have 1, or multiple emails back and forth between our CS department and client.
My python scrip used win32com module to pull emails from Outlook and put them into dataframe, each row as an entry for a unique reported error. After reading (https://learn.microsoft.com/en-us/office/vba/api/outlook.mailitem.conversationid), I decided to go with message.ConversationID. However, the generic email subject means sometimes they would group all unrelated emails together, make ConversationID not really that unique nor useful to me.
Can someone provide me some guide on how best to tackle this issue?
outlook = win32com.client.Dispatch('Outlook.Application').GetNamespace('MAPI')
def message_to_row(message, year, start_month, end_month): # Process each email into row of information
message_time = message.ReceivedTime
winrec_time = message.ReceivedTime
rec_time = pywintypes.Time(winrec_time)
rec_year = rec_time.year
rec_month = rec_time.month
rec_day = rec_time.day
rec_time_string = str(rec_time.hour) + ":" + str(rec_time.minute)
rec_time = format(datetime.datetime.strptime(rec_time_string, "%H:%M"), "%H:%M")
if rec_year >= year:
if rec_month in range(start_month, end_month):
convo_id = message.ConversationID
message_body = message.body.replace("_", " ")
row = [convo_id, rec_year, rec_month,
rec_day, rec_time, message_body]
return row
i am making a management discord bot with discord.py, so i realized that i need to add a command to temp ban someone for some time, this ban can be by roles or by kicking the member out of the channel and then banning him, but i don't know how to do that. Can someone help me?
After a lot of trial and error I finally got it! Given bellow is a discord.py bot with a command to temporarily ban a user and can be used for multiple users
ban_list = []
day_list = []
server_list = []
#This is a background process
async def countdown():
await client.wait_until_ready()
while not client.is_closed:
await asyncio.sleep(1)
day_list[:] = [x - 1 for x in day_list]
for day in day_list:
if day <= 0:
try:
await client.unban(server_list[day_list.index(day)], ban_list[day_list.index(day)])
except:
print('Error! User already unbanned!')
del ban_list[day_list.index(day)]
del server_list[day_list.index(day)]
del day_list[day_list.index(day)]
#Command starts here
#client.command(pass_context = True)
async def ban(ctx,member:discord.Member, days = 1):
if str(ctx.message.author.id) == '<You ID goes here>':
try:
await client.ban(member, delete_message_days=0)
await client.say('User banned for **' + str(days) + ' day(s)**')
ban_list.append(member)
day_list.append(days * 24 * 60 * 60)
server_list.append(ctx.message.server)
except:
await client.say('Error! User not active')
else:
await client.say('You do not have permission to ban users!')
client.loop.create_task(countdown())
I tested this program by banning three users for distinct amounts of time and it worked like a charm. Please note that the time may not be too accurate. The greater the time you choose, the greater the error.
For some reason users that are offline cannot be banned by a Bot.
The bot has to be online full time for this to work... If you reboot the bot or the bot crashes all lists get cleared.
It depends on what you mean by "temporary ban".
Do you want the user actually kicked out and banned from the server for a certain period of time, or do you want the user to be temporarily restricted from certain permissions such as chatting?
I recommend the latter and using the Discord rewrite branch of the API which is new and improved.
Restrict a member via role assignment and unrestrict after x seconds:
#bot.command()
async def restrict(ctx, member:discord.Member, duration: int):
role = discord.utils.get(ctx.guild.roles, name="Restricted")
await member.add_roles(role)
await asyncio.sleep(duration)
await member.remove_roles(role)
Ban a user and unban after x seconds:
#bot.command()
async def ban(ctx, user:discord.User, duration: int):
await ctx.guild.ban(user)
await asyncio.sleep(duration)
await ctx.guild.unban(user)
Keep in mind, if your bot crashes or goes offline for whatever reason while it's in the process of sleeping to unban a user, the bot will not unban the user after it comes back up, so something to consider using may be a database and storing the end time of the ban. You can then query all saved dates during bot startup to figure out how long to sleep for. Also, you will have to get their User object rather than Member object as they are not a part of the guild anymore.
Our errbot is providing links to JIRA tickets when it sees the right ticket patterns. Unfortunately, in slack it is common for users to edit their posts, and if both edits contain the JIRA ticket pattern, errbot provides the link twice, it is annoying.
Can I detect when a message is an edit as opposed to the original message?
Now you can because from the .extra argument on the messages you can get the message ID from slack.
This is not currently possible (on any of the backends), no. For chat networks which allow editing of messages, errbot currently injects edited messages as a new message.
If you need this functionality, please open an issue on errbot's issue tracker so we can brainstorm about how to possibly introduce this functionality.
My bot watches for and expands references to Jira issues too; one of the things I did was keep track of how many messages had been seen in the channel, and when the last time was that I responded to any given issue. Issues expanded within the last N (I use 10) messages are ignored. That way, if they edit the issue key itself they usually get a new expansion, but not if they edit other parts of the message.
def activate(self):
self.messages_seen={} # room: messages seen
self.last_shown={} # issue_key : { room : message number last shown }
super().activate()
def callback_message(self, msg):
if msg.is_group:
try:
self.messages_seen[msg.to.id] += 1
except KeyError:
self.messages_seen[msg.to.id] = 1
def record_expanded(self, msg, key, orig_key=None):
if msg.is_group:
channel=msg.to.id
keys = [ key, orig_key ] if orig_key and orig_key != key else [ key ]
for k in keys:
if k in self.last_shown:
self.last_shown[ k ][ channel ] = self.messages_seen[ channel ]
else:
self.last_shown[ k ] = { channel : self.messages_seen[ channel ] }
def should_expand(self, msg, key):
expand=False
if msg.body.split()[0] == 'jira':
# User said 'jira <key>', so always expand
expand=True
if msg.is_group:
channel=msg.to.id
message_number=self.messages_seen.get(channel, 1)
expanded_in = self.last_shown.get(key, {})
if expanded_in:
if channel not in expanded_in: # key has been expanded, but not in this channel
expand=True
else:
expanded_last_here = message_number - expanded_in[channel]
if expanded_last_here >= self.bot_config.JIRA_EXPAND_ONLY_EVERY: # not recently enough
expand=True
else:
self.log.debug("not expanding '%s' because expanded %d messages ago" % (key, expanded_last_here))
else:
# this key has not been seen anywhere before
expand=True
else:
# direct IM - always expand
expand=True
if not expand:
self.log.debug("Recently expanded %s, suppressing" % key)
return expand