Мессенджер на python
Появилась мысль создать простой чат\мессенджер на Python(+- 100 участников), в следствии чего возник вопрос. Мне придется искать сервер и платить за него, или можно это сделать как-то без него? А если можно без него, то как это реализовать? Попытки найти ответ на мой вопрос в интернете были, но либо я не понял, либо ответа на него не было. Если вы имеете представление как это сделать, прошу помочь, хотя бы книгой, роликом или статьей где будет про это говориться. (я не в коем случае не прошу готовое решение, я просто решил спросить у людей, которые понимают в программировании лучше меня)
Отслеживать
задан 22 июн 2021 в 22:30
193 1 1 серебряный знак 8 8 бронзовых знаков
Всё зависит от ваших целей, технически вам никто не мешает запустить свой мессенджер хоть на обычном телефоне, лишь бы ваши 100 участников могли подключиться к этому телефону
22 июн 2021 в 22:38
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Я пару раз выкладывал концепт p2p месенжера на стековерфлоу. работать можно без сервера вообще, но нужен ещё один месенжер чтоб обменяться контактами для соединения.
Реализуется это через udp и stun. stun пробивает udp порт во внешнюю сеть, а udp позволяет посылать сообщения без установки соединения = без сервера.
Нужно только опубликовать контакт (ip, port) одного из участников и через него получить остальные. Что-то в стиле DHT. Или использовать другой месенжер (например пуши гугла, публичный сип сервер или даже смски) для обмена контактами.
Если вы новичок в сетевом программировании — то начните с месенжера с сервером. Сервер можно найти бесплатно по акции или на начальных тарифах pythonanywhere, oracle и тп. На домашнем интернете есть шанс что у вас белый адрес — можно пробросить порт.
Простой мессенджер на tkinter,socket и threading

В этой статье я бы хотел показать как написать простое приложение мессенджер менее чем в 150 строк.
Серверная часть
Начнём с сервера(наше приложение будет состоять из скриптов сервера и клиента), через который можно получать входящие запросы от клиентов, желающих общаться. Традиционно указываем путь до интерпретатора и импортируем необходимые модули. Конкретно socket и threading. Первый отвечает непосредственно за “общение” процесссов между собой, второй за многопоточность. О этих модулях подробно можно почитать например здесь — socket , threading.
Использование фреймворков, таких как Twisted и SocketServer, было возможным, но мне показалось это излишним для такого простого программного обеспечения, как наше.
#!/usr/bin/env python3 from socket import AF_INET, socket, SOCK_STREAM from threading import Thread
Давайте обозначим константы, отвечающие например за адрес порта и размер буфера.
clients = <> addresses = <> HOST = '' PORT = 33000 BUFSIZ = 1024 ADDR = (HOST, PORT) SERVER = socket(AF_INET, SOCK_STREAM) SERVER.bind(ADDR)
Теперь мы разбиваем нашу задачу на прием новых соединений, рассылку сообщений и обработку определенных клиентов. Давайте начнем с принятия соединений:
def accept_incoming_connections(): """Настраивает обработку для входящих клиентов.""" while True: client, client_address = SERVER.accept() print("%s:%s присоединился к переписке" % client_address) client.send(bytes("Привет!"+ "Введи своё имя и нажми Enter", "utf8")) addresses[client] = client_address Thread(target=handle_client, args=(client,)).start()
Это просто цикл, который всегда ждет входящих соединений и, как только он его получает, регистрирует соединение (печатает некоторые сведения о соединении) и отправляет подключенному клиенту приветствие. Затем он сохраняет адрес клиента и позже запускает поток обработки для этого клиента. Конечно, мы еще не определили для этого целевую функцию handle_client (), но вот как мы это сделаем:
def handle_client(client): name = client.recv(BUFSIZ).decode("utf8") welcome = 'Добро пожаловать %s! если желаете покинуть чат то, нажмите чтобы выйти.' % name client.send(bytes(welcome, "utf8")) msg = "%s Теперь в переписке" % name broadcast(bytes(msg, "utf8")) clients[client] = name while True: msg = client.recv(BUFSIZ) if msg != bytes(" ", "utf8"): broadcast(msg, name+": ") else: client.send(bytes(" ", "utf8")) client.close() del clients[client] broadcast(bytes("%s покинул переписку." % name, "utf8")) break
Естественно, после того, как мы отправим новому клиенту приветственное сообщение, он ответит именем, которое он хочет использовать для дальнейшего общения. В функции handle_client () первая задача, которую мы делаем, — мы сохраняем это имя, а затем отправляем клиенту еще одно сообщение о дальнейших инструкциях. После этого идет основной цикл: здесь мы получаем дополнительные сообщения от клиента и, если сообщение не содержит инструкций для выхода, мы просто передаем сообщение другим подключенным клиентам (мы определим метод широковещания через минуту ). Если мы сталкиваемся с сообщением с инструкциями выхода (то есть клиент отправляет ), мы возвращаем то же самое сообщение клиенту, а затем мы закрываем сокет подключения для него. Затем мы делаем очистку, удаляя запись для клиента, и, наконец, сообщаем другим связанным людям, что этот конкретный человек покинул чат.
Теперь пропишем функцию broadcast ():
def broadcast(msg, prefix=""): for sock in clients: sock.send(bytes(prefix, "utf8")+msg)
Эта функция просто отправляет сообщение всем подключенным клиентам и при необходимости добавляет дополнительный префикс. Мы передаем префикс для broadcast () в нашей функции handle_client () и делаем это так, чтобы люди могли точно знать, кто является отправителем конкретного сообщения. Это были все необходимые функции для нашего сервера. Наконец, мы добавили код для запуска нашего сервера и прослушивания входящих соединений:
if __name__ == "__main__": SERVER.listen(5) print("Ожидание соединения") ACCEPT_THREAD = Thread(target=accept_incoming_connections) ACCEPT_THREAD.start() # Бесконечный цикл. ACCEPT_THREAD.join() SERVER.close()
Мы присоединяемся к ACCEPT_THREAD, чтобы основной скрипт ожидал его завершения и не переходил на следующую строку, которая закрывает сервер. Это завершает наш серверный скрипт.
В итоге получаем вот такой код для серверной части:
#!/usr/bin/env python3 from socket import AF_INET, socket, SOCK_STREAM from threading import Thread def accept_incoming_connections(): while True: client, client_address = SERVER.accept() print("%s:%s соединено" % client_address) client.send(bytes("Добро пожаловать , введите своё имя и нажмите Enter", "utf8")) addresses[client] = client_address Thread(target=handle_client, args=(client,)).start() def handle_client(client): name = client.recv(BUFSIZ).decode("utf8") welcome = 'Добро пожаловать %s! Если желаете выйти,то нажмите чтобы выйти.' % name client.send(bytes(welcome, "utf8")) msg = "%s вступил в переписку" % name broadcast(bytes(msg, "utf8")) clients[client] = name while True: msg = client.recv(BUFSIZ) if msg != bytes(" ", "utf8"): broadcast(msg, name+": ") else: client.send(bytes(" ", "utf8")) client.close() del clients[client] broadcast(bytes("%s покинул переписку" % name, "utf8")) break def broadcast(msg, prefix=""): for sock in clients: sock.send(bytes(prefix, "utf8")+msg) clients = <> addresses = <> HOST = '' PORT = 33000 BUFSIZ = 1024 ADDR = (HOST, PORT) SERVER = socket(AF_INET, SOCK_STREAM) SERVER.bind(ADDR) if __name__ == "__main__": SERVER.listen(5) print("ожидание соединения") ACCEPT_THREAD = Thread(target=accept_incoming_connections) ACCEPT_THREAD.start() ACCEPT_THREAD.join() SERVER.close()
Клиентская часть###
Теперь приступим к наиболее интересной части нашего приложения — к клиенту. В качестве gui будем использовать tkinter, т.к в нём довольно легко построить несложное приложение. Традиционно импортируем модуль tkinter, а также модули использовавшиеся ранее при написании серверной части программы.
#!/usr/bin/env python3 from socket import AF_INET, socket, SOCK_STREAM from threading import Thread import tkinter
Теперь мы напишем функции для обработки отправки и получения сообщений. Начнем с получения:
def receive(): """обработка получения сообщений""" while True: try: msg = client_socket.recv(BUFSIZ).decode("utf8")# декодируем,чтобы не получить кракозябры msg_list.insert(tkinter.END, msg) except OSError: break
Почему мы снова используем бесконечный цикл? Потому что мы будем получать сообщения совершенно независимо от того, как и когда мы отправляем сообщения. Мы не хотим, чтобы это было приложение для чата с функциональностью уровня рации. Мы хотим приложение в котором можно отправлять или получать сообщения одновременно; мы хотим получать сообщения, когда сами того пожелаем, и отправлять их, когда захотим.
Функциональность внутри цикла довольно проста; recv () является блокирующей частью. Он останавливает выполнение до тех пор, пока не получит сообщение, а когда это произойдет, мы продвигаемся вперед и добавляем сообщение в msglist. Затем мы определяем msg_list, который является функцией Tkinter для отображения списка сообщений на экране. Далее мы определим функцию send ():
def send(event=None): """обработка отправленных сообщений""" msg = my_msg.get() my_msg.set("") # очищаем поле. client_socket.send(bytes(msg, "utf8")) if msg == " ": client_socket.close() top.quit()
my_msg — это поле ввода в графическом интерфейсе, и поэтому мы извлекаем сообщение для отправки с помощью msg = my_msg.get (). После этого мы очищаем поле ввода и затем отправляем сообщение на сервер, который, как мы видели ранее, передает это сообщение всем клиентам (если это не сообщение о выходе). Если это сообщение о выходе, мы закрываем сокет, а затем приложение с графическим интерфейсом (через top.close ())
Мы определяем еще одну функцию, которая будет вызываться, когда мы решим закрыть окно с GUI. Это своего рода функция очистки до закрытия, которая закрывает соединение с сокетом до закрытия графического интерфейса:
def on_closing(event=None): """Эта функция вызывается когда закрывается окно""" my_msg.set(" ") send()
Это устанавливает в поле ввода значение , а затем вызывает send (). Начнем с определения виджета верхнего уровня и установки его заголовка, как и в любой другой программе на tkinter:
top = tkinter.Tk() top.title("TkMessenger")
Затем создаём фрейм со списком сообщений, поле для ввода сообщений и скроллбар для перемещения по истории переписки
messages_frame = tkinter.Frame(top) my_msg = tkinter.StringVar() my_msg.set("Введите ваше сообщение здесь.") scrollbar = tkinter.Scrollbar(messages_frame)#скроллбар
“Упаковываем” наши элементы и размечаем их расположение в окне:
msg_list = tkinter.Listbox(messages_frame, height=15, width=50, yscrollcommand=scrollbar.set) scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y) msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH) msg_list.pack() messages_frame.pack()
После этого мы создаем поле ввода для пользователя, чтобы ввести свое сообщение, и привязать его к строковой переменной, определенной выше. Мы также привязываем его к функции send (), чтобы всякий раз, когда пользователь нажимает return, сообщение отправлялось на сервер.
Далее мы создаем кнопку отправки, если пользователь желает отправить свои сообщения, нажав на нее. Опять же, мы связываем нажатие этой кнопки с функцией send ().
И да, мы также упаковываем все то, что создали только сейчас. Кроме того, не забудьте использовать функцию очистки on_closing (), которая должна вызываться, когда пользователь хочет закрыть окно GUI:
entry_field = tkinter.Entry(top, textvariable=my_msg) entry_field.bind("", send) entry_field.pack() send_button = tkinter.Button(top, text="отправить", command=send) send_button.pack() top.protocol("WM_DELETE_WINDOW", on_closing)
И вот мы подходим к завершению. Мы еще не написали код для подключения к серверу. Для этого мы должны запросить у пользователя адрес сервера. Я сделал это, просто используя input (), чтобы пользователь встретился с подсказкой командной строки, запрашивающей адрес хоста перед запуском окна с графическим интерфейсом. В будущем можно добавить виджет для этой цели. А пока вот так:
HOST = input('Введите хост: ') PORT = input('Введите порт: ') if not PORT: PORT = 33000 # Стандартный порт else: PORT = int(PORT) BUFSIZ = 1024 ADDR = (HOST, PORT) client_socket = socket(AF_INET, SOCK_STREAM) client_socket.connect(ADDR)
Как только мы получаем адрес и создаем сокет для подключения к нему, мы запускаем поток для получения сообщений, а затем основной цикл для нашего приложения с графическим интерфейсом:
receive_thread = Thread(target=receive) receive_thread.start() tkinter.mainloop()
Вот и всё! Теперь наш скрипт клиентской части выглядит вот так:
#!/usr/bin/env python3 from socket import AF_INET, socket, SOCK_STREAM from threading import Thread import tkinter def receive(): while True: try: msg = client_socket.recv(BUFSIZ).decode("utf8") msg_list.insert(tkinter.END, msg) except OSError: break def send(event=None): msg = my_msg.get() my_msg.set("") client_socket.send(bytes(msg, "utf8")) if msg == " ": client_socket.close() top.quit() def on_closing(event=None): my_msg.set(" ") send() top = tkinter.Tk() top.title("TkMessenger") messages_frame = tkinter.Frame(top) my_msg = tkinter.StringVar() my_msg.set("Введите ваше сообщение здесь") scrollbar = tkinter.Scrollbar(messages_frame) msg_list = tkinter.Listbox(messages_frame, height=15, width=50, yscrollcommand=scrollbar.set) scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y) msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH) msg_list.pack() messages_frame.pack() entry_field = tkinter.Entry(top, textvariable=my_msg) entry_field.bind("", send) entry_field.pack() send_button = tkinter.Button(top, text="отправить", command=send) send_button.pack() top.protocol("WM_DELETE_WINDOW", on_closing) HOST = input('Введите хост: ') PORT = input('Введите порт: ') if not PORT: PORT = 33000 else: PORT = int(PORT) BUFSIZ = 1024 ADDR = (HOST, PORT) client_socket = socket(AF_INET, SOCK_STREAM) client_socket.connect(ADDR) receive_thread = Thread(target=receive) receive_thread.start() tkinter.mainloop()
Да, наше приложение не может тягаться с такими гигантами как: telegram, viber, клиентами xmpp/jabber; однако нам удалось создать простой чат, который каждый может развить в что-то своё: сделать уклон в безопасность(например шифруя передаваемые пакеты) или в хороший ux/ui. Получилась своего рода база для чего-то большего и это круто. Спасибо за прочтение, буду рад любым замечаниям и пожеланиям. Традиционно исходный код программы доступен в моём репозитории на github.
Создание своего мессенджера на Python с нуля


Публиковать могут пользователи с любым рейтингом. Однако!
Приветствуется:
• уважение к читателям и авторам
• простота и информативность повествования
• тег python2 или python3, если актуально
• код публиковать в виде цитаты, либо ссылкой на специализированный сайт
Не рекомендуется:
• допускать оскорбления и провокации
• распространять вредоносное ПО
• просить решить вашу полноценную задачу за вас
2 года назад
Без текста минус нахуй.
раскрыть ветку
Лучшие посты за сегодня
21 час назад

Праздник
Вчера друг позвал в гости. На вопрос «какой повод», ответил — сочельник и Рождество. Друг татарин. За столом были 5 татар, 1 русский и 1 чувашин. Первый тостующий начал говорить на ломаном русском, на что русский сказал «татарча сойлэшегез, мин анлыйм» (говорите по татарски, я всё понимаю). А чувашин шпарит на татарском, как на родном. В полночь пошли к церкви, но заходить не стали, посчитали, что в подпитом состоянии это неправильно и харам. Посмотрели салют и пошли в баню. Я это к чему — для нормальных людей нет никаких различий и по национальности и по религии. Обожаю свой Татарстан.
23 часа назад
Пост благодарности!
31 декабря 2023г., старший 5 лет ,с утра температурит 39.5 ,как потом выяснилось ,в пятницу 29,в садике с группой единомышленников-гурманов наелся снега,шлифанули льдом.
Послеобеденный сон,приступ кашля,непрекращающегося, иду с ним в ванную(умыть ,сплюнуть) ребенок заливатся кашлем, начинает синеть и плакать.паника просто пдц.
Вызываю скорую,диспетчер услышав причину.и пресекая панику выдает- слушаем меня,отвечаем на вопросы. вызов принят 15-08.
Спустя 15 минут приехали две девушки и парень. Я с младшим на кухню,жена с ними. Незнаю че делали,укол или еще чего,но дите в себя пришел, засуетился)
Увезли в больницу,там анализы, рентген — бронхоспзм и односторонняя пневмония,госпитализация.
Вчером со скорой звонили мне на телефон ,узнать как дела у Руслана)
Так вот,хочу выразить огромную БЛАГОДАРНОСТЬ всем медработникам дежуривштм 31 декабря,а в частности бригаде из г Новосибирска,принявшей вызов на ул.Немировича, к мальчику 5 лет. Вы спасли ему жизнь!
P.S.Вчера нас выписали)
10 часов назад
К кому сегодня скорая не приехала ?
Только что вернулся с пвз Вайлдберриз.
Нахожусь немного в шоке. Бабулька выписала аппарат для измерения давления и чтобы проверить его, вызвала скорую чтоб они произвели замеры ей и она смогла сравнить с показаниями товара. Врач приехавший ничего не сказал, измерил давление и уехал, хоть и видно было что у него глаз задёргался.
Поддержать
16 часов назад
Заруба
В декабре заехал в новый ЖК.
На этаже из восьми квартир только моя и соседняя трешка обживаются. Пересеклись с соседом. Оказалось то семья из 6 человек. Родители и 4 мелких. Почти как Барбоскины-все разные.
Оказалось,он какой то христианский священнослужитель в неплохом ранге. Я не разбираюсь. Так вот родной только один. Пока беседовали,он активно намекал на желание видеть меня среди его прихожан.
Я уклончиво и вежливо объяснял,что пока ни времени,ни денег нет на обретении мною бога. Он не унимается. Вижу,что глаза горят. Видит во мне цель.
Ну это ладно. Перед новым годом в квартиру напротив его заехала семья. Я даже посчитать не успел. Человек 10. По всем признакам ортодоксальные мусульмане.
Пилотной серией сериала был выпуск,когда я с ними в лифте ехал.
За 11 этажей вниз они сначала спокойно,а потом чуть не до крика орали мне в уши с разных сторон. Жду когда начнут пиздиться.
08.01.2024. 02:15 : пока атеист.
P.S: Если заедут сатанисты на этаж,подрублю донат))))
19 часов назад
Вот до боли в груди (3 размера) уже это одиночество одолело
Привет, я @bazinda тг
Несколько лет назад стала инвалидкой, была овощем, врачи обещали , что такой на всю жизнь и останусь, а я не куя, всех обманула и бодрячком)
Сейчас хромая ходячая, жирненькая немножко, но живая))
Очень прошу пост приподнять и обнять , красавчики и красотки ))
Вдруг есть извращенцы меня желающие)
Всех обнимаю , целую и уж поверьте, все ваши мечты обязательно сбудутся, я подсуетилась нашаманила в рождество со своей удачей)
22 часа назад

Сестры

Показать полностью 1
Поддержать
18 часов назад
Ответ на пост «Тупые среди нас»
Ненароком вспомнил одного претендента на звание «самый тупой человек, с которым я общался».
Это был 2011 год. Год, когда мы всей страной наблюдали второе пришествие МММ. Ажиотаж был нехилый, открывались офисы-однодневки, а тупые либо жадные люди несли туда денежки. Все это ещё приправлялось историями типа «Какой-то тип продал квартиру, вложил миллион, через два месяца вывел 30 миллионов и уехал жить на ГОА!»
Я работал в автосалоне одного официального дилера. Ну и само собой были у нас работники сервиса, которые проводили ТО, гарантийные ремонты, и прочую фигню. И был там электрик Андрей. Обычный тщедушный бедолага в вечно засаленной одёжке. Мы с ним не общались особо никогда, только здоровались-прощались.
И вот в один день он вдруг начинает ходить вокруг новой модели авто, которая появилась в продаже буквально как неделю. Кроссовер, стоимостью около 800-900т.р. по тем временам. Андрей ковыряется везде, каждый уголок машины осматривает. Я решил подойти.
Я: Что, машину решил изучить, чтоб знать че где крутить?
Андрей-электрик: Да не, купить думаю.
Я: Ого, молодец, кредит считал уже?
А: Не, я за наличку! Я в МММ 30 тысяч вложил, через 10 месяцев 5 миллионов заберу. Куплю вот машину, квартиру в центре Москвы и поедем с женой туда жить!
Это сейчас я бы просто кивнул и стал тратить время на дурака, а тогда я был моложе и инициативнее) Ну и решил ему рассказать что пирамида хуета, что не может система выплачивать такие дивиденды в геометрической прогрессии. Не говоря о том что на квартиру бы ему там хрен хватило в центре Москвы)
Я: Ты представляешь сколько людей должны занести вклады, чтоб тебе одному выплатить такие лютые проценты? Тысячи! А таких как ты тоже тысячи! Это всë схлопнется скоро!
Дальше его реальные слова! Прям как он и говорил!
А: Не схлопнется! У нас в России народа миллиардов 5! Это долго ещё работать будет!
Я (знатно охуев): Какие 5 миллиардов? В Китае и то миллиард. А у нас 140 млн всего.
А: Да конечно! Ты на карту посмотри! В таком маленьком Китае миллиард, а у нас вон какая страна здоровая, по-любому больше население!
И напоследок выдал вишенку на торте: Ты в любой город наш приедь, выйди на вокзал и посмотри сколько много людей ходит! И это в каждом городе! По-любому миллиардов 5 есть.
Я понял что тут реально клинический случай, не стал спорить. Просто посоветовал на досуге почитать про нашу страну, про плотность населения и т. д.
Через какое-то время МММ рухнула, он, конечно, проебал свои деньги, но навсегда останется в моей душе как один из самых тупых людей, с которыми мне приходилось вести беседу 🙂
Показать полностью
19 часов назад

Костюм для себя
Смотрю фото тут нравятся больше чем видосы. тогда вот еще.

Костюм сшил из футера с начесом. Сшил я его больше года назад.Верхняя часть, что с ромбиками, это лоскутное шитье. Не стежка. По факту это мой третий или четвертый костюм. И первый, который я сшил на оверлоке. На тот момент за машинкой уже 2 месяца шил, а на оверлоке 2 дня))

Основа штанов и куртки была куплена готовыми выкройками. А вот вставки уже моделировал сам. Шапка вообще с картинки в тырнете.

Костюм таскаю уже год. Много раз стирал. Вид имеет.
И не в оправдание, а по факту — я самоучка. Учусь по видео на ютюбе и PDF файлам к выкройкам. Шью чуть больше года.
Показать полностью 3
Поддержать
9 часов назад

Уголовное дело против 36 -летней «пивной королевы» прекращено в связи с истечением срока давности

Лариса Горностаева, депутат Омского горсовета и владелица пивзавода «Сибирский стандарт» обвинялась в уклонении от уплаты налогов в особо крупном размере (487 млн рублей). Следствие выявило, что по ее распоряжению в бухгалтерской документации и налоговой отчетности не фиксировался весь объем произведенного пива. Излишки реализовывали через подконтрольные фирмы. Для реализации излишков «налево» использовался даже потайной водопровод. Обвинили Горностаеву еще в 2020 году. Расследование дела ранее то останавливалось, то возобновлялось. Судебный процесс длился полтора года. Он периодически замедлялся по причине многократных отлагательств адвоката, неожиданных перерывов судьи и болезней подсудимой. В результате адвокат подсудимой Наумов обратился с ходатайством о закрытии дела в связи с истечением срока давности. Судья Русинова удовлетворила запрос. Дело закрыли. Горностаева до сих пор является единственным владельцем пивзавода «Сибирский стандарт». Ссылка:
Создание messengers бота на Python. Полное руководство
Функционал из данного гайда был перенесен на messengers Business API, с помощью которого вы также сможете производить рассылки, создавать чат боты и многое другое, но без риска быть заблокированным. В связи с изменением условий работы API, в данном гайде частично могут использоваться неактуальные сведения. Пожалуйста, посмотрите документацию перед началом работы. Сейчас Chat API предлагает самый доступный и автоматизированный messengers Business API на рынке с Многопользовательским Чатом, Визуальным конструктором ботов, готовыми интеграциями приложений и другими полезными функциями.
- Вывод списка команд
- Вывод ID текущего чата
- Вывод текущего времени сервера, на котором работает бот.
- Вывод вашего имени
- Отправка файлов разных форматов (pdf, jpg, doc, mp3 и т.д.)
- Отправка заранее записанных голосовых сообщений
- Отправка гео-координат (локации)
- Создание конференции (группы)
Внимание: чтобы бот работал, телефон должен быть всегда подключен к интернету и не должен использоваться для messengers Web
Подготовительная работа
В самом начале, сразу свяжем messengers с нашим скриптом, чтобы по мере написания кода — проверять его работу. Для этого переходим в личный кабинет и получаем там QR-код. Далее открываем messengers на мобильном телефоне, заходим в Настройки -> messengers Web -> Сканируем QR-код.
Теперь, чтобы сервер вызывал наш скрипт при новых сообщениях, нужно указать WebHook URL. WebHook URL – это ссылка, куда будут посылаться, методом POST, JSON–данные с информацией о входящих сообщениях или уведомлениях. Соответственно, чтобы бот работал нам требуется сервер – который эти данные будет принимать и обрабатывать. При написании этой статьи, мы развернули у себя сервер с помощью микро-фраемворка FLASK. Он позволяет удобно реагировать на входящие запросы и обрабатывать их.
Пишем инициализацию класса бота
Создадим файл “wabot.py” и в нем опишем класс для нашего бота. Нам потребуется импортировать данные библиотеки:
import json import requests import datetime
Библиотека json отвечает за обработку формата Json. Requests – Нужен нам для того, чтобы обращаться к API сайта.
Далее создадим класс WABot
class WABot(): def __init__(self, json): self.json = json self.dict_messages = json['messages'] self.APIUrl = 'https://eu41.chat-api.com/instance12345/' self.token = 'abcdefg'

В нем мы опишем конструктор класса, который по умолчанию будет принимать json – тот самый, что будет содержать информацию о входящих сообщениях (Его будет принимать Webhook и передавать в наш класс). Для того, чтобы узнать, как будет выглядеть принимаемый json – можно зайти в удобный раздел тестирования, который мы предоставляем в личном кабинете. В нем можно протестировать запросы и Webhook.
Здесь мы присваиваем атрибутам класса значения. Dict_messages – Словарь, который содержит информацию из сообщения в json файле, который мы приняли
self.json = json self.dict_messages = json['messages']
Посмотреть структуру json можно в разделе тестирования в элементе «Проверка WebHook». Требуется запустить проверку и отправлять в ваш messengers чат сообщения. На экране будет выводиться json, посылаемый к webhook.
self.APIUrl = 'https://eu41.chat-api.com/instance12345/' self.token = 'abcdefg'
Вам требуется подставить свои данные APIUrl и token, их мы будем использовать для формирования запроса к API.
Пишем функционал бота
Отправка запросов
Для работы нам потребуется отправлять запросы к API. Напишем функцию, которая будет формировать запросы и отправлять их, исходя из наших параметров.
def send_requests(self, method, data): url = f"?token=" headers = answer = requests.post(url, data=json.dumps(data), headers=headers) return answer.json()
send_requests — принимает два параметра: method и data.
- method определяет, какой метод ChatAPI должен быть вызван.
- data содержит необходимые для пересылки данные.
Подробнее о всех методах можно почитать в документации. Data – это словарь данных из которых мы сформируем json и передадим методом Post на сервер. (Т.к. для нашего функционала бота требуются лишь Post методы, только ими мы и будем пользоваться)
На данном этапе мы формируем строку запроса к API.
url = f"?token="
Далее обязательно нужно указать header Contet-Type и пометить его Application/Json, так как мы будем всегда передавать наши данные форматом json.
headers =
Теперь формируем полноценный запрос с помощью requests.post и передаем на сервер api наши данные. Json.dump(data) – серриализует наш словарь data в формат json.
answer = requests.post(url, data=json.dumps(data), headers=headers) return answer.json()
Возвращает функции ответ сервера в формате json.
Отправка сообщений
send_message — принимает два параметра: chatId и text.
- chatId – Id чата, в который необходимо отправить сообщение
- Text – Текст сообщения
Теперь напишем метод, который позволит отправлять сообщения в наш чат.
def send_message(self, chatId, text): data = answer = self.send_requests('sendMessage', data) return answer
Формируем словарь data, который содержит в себе тело “chatId” — Id, куда требует отправить сообщение и тело ‘body’ с необходимым нам текстом.
data =
Далее передаем наши данные в метод, который мы написали в предыдущем этапе
answer = self.send_requests('sendMessage', data) return answer
Для того чтобы отправить сообщение в Chat Api используется метод «sendMessage», поэтому его мы и передаем в функции в качестве параметра вместе с нашим словарем data. И возвращаем ответ сервера.
Приветствие
Метод welcome будем вызывать по команде “hi” бота и при вводе несуществующей команды.
- chatId – Id чата, в который необходимо отправить сообщение
- noWelcome – Булева переменная, определяющая какой текст будет отправлен в чат: приветствие или список команд. По умолчанию False.
def welcome(self,chatId, noWelcome = False): welcome_string = '' if (noWelcome == False): welcome_string = "messengers Demo Bot Python\n" else: welcome_string = """Incorrect command Commands: 1. chatId - show ID of the current chat 2. time - show server time 3. me - show your nickname 4. file [format] - get a file. Available formats: doc/gif/jpg/png/pdf/mp3/mp4 5. ptt - get a voice message 6. geo - get a location 7. group - create a group with the bot""" return self.send_message(chatId, welcome_string)
Формируем нашу строку с сообщением исходя из переменной noWelcome и передаем в функцию send_message в качестве отправляемого текста.
Вывод chatId
def show_chat_id(self,chatId): return self.send_message(chatId, f"Chat ID : ")
Вывод времени
def time(self, chatId): t = datetime.datetime.now() time = t.strftime('%d:%m:%Y') return self.send_message(chatId, time)
Функция me
Выводит информацию о имени собеседника по команде ‘me’
def me(self, chatId, name): return self.send_message(chatId, name)
Функция file
Отправляет файл с указанным форматом в диалог
- chatId – Id чата, в который необходимо отправить сообщение
- format – формат файла, который необходимо отправить. Все отправляемые файлы хранятся на сервере.
def file(self, chatId, format): availableFiles = if format in availableFiles.keys(): data = < 'chatId' : chatId, 'body': f'https://domain.com/Python/', 'filename' : availableFiles[format], 'caption' : f'Get your file ' > return self.send_requests('sendFile', data)
Здесь мы сформировали словарь, который содержит в качестве ключей необходимые нам форматы, а в качестве значений имена файлов, которые лежат на сервере и ждут отправки:
availableFiles =
Далее проверяем существует ли в нашем словаре формат, который передал пользователь
Если существует – то формируем запрос на отправку файла, где:
if format in availableFiles.keys(): data = < 'chatId' : chatId, 'body': f'https://domain.com/Python/', 'filename' : availableFiles[format], 'caption' : f'Get your file ' > return self.send_requests('sendFile', data)
- chatId – Id чата, в который необходимо отправить сообщение
- Body – прямая ссылка до файла, который необходимо отправить
- Filename – имя файла
- Caption – текст, который будет отправлен вместе с файлом
Формируем запрос send_requests с параметром “sendFile” и передаем в него наши данные.
Функция ptt
Отправляет голосовое сообщение в диалог
def ptt(self, chatId): data = < "audio" : 'https://domain.com/Python/ptt.ogg', "chatId" : chatId >return self.send_requests('sendAudio', data)
Формируем словарь наших данных, где:
- chatId – Id чата, в который необходимо отправить сообщение
- audio – прямая ссылка на файл формата ogg
Отправляем запрос к api методом “sendAudio”
Функция geo
def geo(self, chatId): data = < "lat" : '51.51916', "lng" : '-0.139214', "address" :'Ваш адрес', "chatId" : chatId >answer = self.send_requests('sendLocation', data) return answer
- chatId – Id чата, в который необходимо отправить сообщение
- lat – заранее заданные координаты
- lng – заранее заданные координаты
- address – ваш адрес или любая необходимая вам строка.
После формирования словаря, отправляем запрос к API методом “sendLocation”
Функция group
Создает группу, в которой будете вы и бот
def group(self, author): phone = author.replace('@c.us', '') data = < "groupName" : 'Group with the bot Python', "phones" : phone, 'messageText' : 'It is your group. Enjoy' >answer = self.send_requests('group', data) return answer
author – тело json, посылаемое webhook, содержит информацию о том, кто отправил сообщение.
Данное тело содержит в себе информацию о номере пользователя, но с дополнительными символами. Вызовем функцию replace и удалим их, оставив лишь номер телефона:
phone = author.replace('@c.us', '')
data =
- groupName – имя конференции после её создания
- phones – телефоны необходимых участников конференции, можно передавать массив из нескольких телефонов
- messageText – Первое сообщение в конференции
Отправляем запрос методом ‘group’
Обрабатываем запросы пользователей
Весь функционал нашего демо-бота мы описали, теперь требуется организовать логику работы бота, чтобы он мог реагировать на команды и взаимодействовать с пользователем. Для этого опишем еще одну функцию
def processing(self): if self.dict_messages != []: for message in self.dict_messages: text = message['body'].split() if not message['fromMe']: if text[0].lower() == 'hi': return self.welcome(id) elif text[0].lower() == 'time': return self.time(id) elif text[0].lower() == 'chatId': return self.show_chat_id(id) elif text[0].lower() == 'me': return self.me(id, message['senderName']) elif text[0].lower() == 'file': return self.file(id, text[1]) elif text[0].lower() == 'ptt': return self.ptt(id) elif text[0].lower() == 'geo': return self.geo(id) elif text[0].lower() == 'group': return self.group(message['author']) else: return self.welcome(id, True) else: return 'NoCommand'
Данную функцию будем вызывать каждый раз, когда будем получать данные в наш webhook.
Разберем её по порядку:
Помните атрибут нашего бота dict_messages, который мы создали в самом начале? Он содержит в себе словари сообщений, которые мы приняли. Данная проверка отсеивает данные, которые не содержат в себе сообщений. Так как к webhook может прийти запрос без сообщения.
if self.dict_messages != []:
В действительности нам может прийти несколько сообщений в одном запросе, и наш бот должен обработать их все. Для этого мы перебираем все словари, который содержит в себе лист dict_messages.
for message in self.dict_messages: text = message['body'].split()
После вхождения в цикл мы объявляем переменную text – которая будет являться листом слов, содержащихся в нашем сообщении. Для этого мы обращаемся к словарю message по ключу [‘body’], чтобы получить текст входящего сообщения и просто вызываем функцию split(), которая позволит разбить текст на слова.
Далее мы делаем проверку, что входящее сообщение не от нас самих, посредством обращения к ключу ‘fromMe”, который содержит в себе True или False и проверяет от кого было сообщение.

Если же данной проверки не будет бот может уйти в бесконечную рекурсию.
Теперь мы получаем id чата из все того же самого словаря message по ключу [‘chatId’]. Обращаемся к первому элементу листа слов, приводим его в нижний регистр, чтобы бот мог реагировать на сообщения написанное КАПСОМ или ЗаБоРчИкОм и сравниваем его с необходимыми нам командами.
После сравнения просто вызываем функционал, который мы описали на предыдущих этапах с параметром id.
Поздравляю, наш бот готов!) Теперь мы можем полноценно реагировать на входящие сообщения.
Вам необходимо только подставить свой токен из личного кабинета и номер инстанса.
Сервер Flask
Для обработки запросов будем использовать сервер Flask. Создадим файл app.py, импортируем все необходимые библиотеки.
from flask import Flask, request, jsonify from wabot import WABot import json app = Flask(__name__) @app.route('/', methods=['POST']) def home(): if request.method == 'POST': bot = WABot(request.json) return bot.processing() if(__name__) == '__main__': app.run()
Инициализируем переменную app, которая будет являться классом Flask.
app = Flask(__name__) @app.route('/', methods=['POST']) def home(): if request.method == 'POST': bot = WABot(request.json) return bot.processing()
И пропишем для неё путь app.route(‘/’, methods = [‘POST’]). Данный декоратор означает, что наша функция home будет вызываться каждый раз, когда к нашему серверу flask будут обращаться посредством post запроса по главному пути.
Делаем проверку того, что к серверу обратились с помощью метода POST. Создаем экземпляр нашего бота и передаем в него json данные.
requests.json – позволяет получить json файлы из тела запроса, который был передан к нашему серверу.
def home(): if request.method == 'POST': bot = WABot(request.json) return bot.processing()
И теперь просто вызываем у нашего объекта метод bot.processing(), который отвечает за обработку запросов.
messengers бот на Python
Теперь необходимо загрузить наш сервер вместе с ботом на хостинг и в качестве webhook указать ваш домен. При каждом входящем сообщение на сервер будут приходить и обрабатываться данные.
Простой мессенджер на tkinter,socket и threading

В этой статье я бы хотел показать как написать простое приложение мессенджер менее чем в 150 строк.
Серверная часть
Начнём с сервера(наше приложение будет состоять из скриптов сервера и клиента), через который можно получать входящие запросы от клиентов, желающих общаться. Традиционно указываем путь до интерпретатора и импортируем необходимые модули. Конкретно socket и threading. Первый отвечает непосредственно за “общение” процесссов между собой, второй за многопоточность. О этих модулях подробно можно почитать например здесь — socket , threading.
Использование фреймворков, таких как Twisted и SocketServer, было возможным, но мне показалось это излишним для такого простого программного обеспечения, как наше.
#!/usr/bin/env python3 from socket import AF_INET, socket, SOCK_STREAM from threading import Thread
Давайте обозначим константы, отвечающие например за адрес порта и размер буфера.
clients = <> addresses = <> HOST = '' PORT = 33000 BUFSIZ = 1024 ADDR = (HOST, PORT) SERVER = socket(AF_INET, SOCK_STREAM) SERVER.bind(ADDR)
Теперь мы разбиваем нашу задачу на прием новых соединений, рассылку сообщений и обработку определенных клиентов. Давайте начнем с принятия соединений:
def accept_incoming_connections(): """Настраивает обработку для входящих клиентов.""" while True: client, client_address = SERVER.accept() print("%s:%s присоединился к переписке" % client_address) client.send(bytes("Привет!"+ "Введи своё имя и нажми Enter", "utf8")) addresses[client] = client_address Thread(target=handle_client, args=(client,)).start()
Это просто цикл, который всегда ждет входящих соединений и, как только он его получает, регистрирует соединение (печатает некоторые сведения о соединении) и отправляет подключенному клиенту приветствие. Затем он сохраняет адрес клиента и позже запускает поток обработки для этого клиента. Конечно, мы еще не определили для этого целевую функцию handle_client (), но вот как мы это сделаем:
def handle_client(client): name = client.recv(BUFSIZ).decode("utf8") welcome = 'Добро пожаловать %s! если желаете покинуть чат то, нажмите чтобы выйти.' % name client.send(bytes(welcome, "utf8")) msg = "%s Теперь в переписке" % name broadcast(bytes(msg, "utf8")) clients[client] = name while True: msg = client.recv(BUFSIZ) if msg != bytes(" ", "utf8"): broadcast(msg, name+": ") else: client.send(bytes(" ", "utf8")) client.close() del clients[client] broadcast(bytes("%s покинул переписку." % name, "utf8")) break
Естественно, после того, как мы отправим новому клиенту приветственное сообщение, он ответит именем, которое он хочет использовать для дальнейшего общения. В функции handle_client () первая задача, которую мы делаем, — мы сохраняем это имя, а затем отправляем клиенту еще одно сообщение о дальнейших инструкциях. После этого идет основной цикл: здесь мы получаем дополнительные сообщения от клиента и, если сообщение не содержит инструкций для выхода, мы просто передаем сообщение другим подключенным клиентам (мы определим метод широковещания через минуту ). Если мы сталкиваемся с сообщением с инструкциями выхода (то есть клиент отправляет ), мы возвращаем то же самое сообщение клиенту, а затем мы закрываем сокет подключения для него. Затем мы делаем очистку, удаляя запись для клиента, и, наконец, сообщаем другим связанным людям, что этот конкретный человек покинул чат.
Теперь пропишем функцию broadcast ():
def broadcast(msg, prefix=""): for sock in clients: sock.send(bytes(prefix, "utf8")+msg)
Эта функция просто отправляет сообщение всем подключенным клиентам и при необходимости добавляет дополнительный префикс. Мы передаем префикс для broadcast () в нашей функции handle_client () и делаем это так, чтобы люди могли точно знать, кто является отправителем конкретного сообщения. Это были все необходимые функции для нашего сервера. Наконец, мы добавили код для запуска нашего сервера и прослушивания входящих соединений:
if __name__ == "__main__": SERVER.listen(5) print("Ожидание соединения") ACCEPT_THREAD = Thread(target=accept_incoming_connections) ACCEPT_THREAD.start() # Бесконечный цикл. ACCEPT_THREAD.join() SERVER.close()
Мы присоединяемся к ACCEPT_THREAD, чтобы основной скрипт ожидал его завершения и не переходил на следующую строку, которая закрывает сервер. Это завершает наш серверный скрипт.
В итоге получаем вот такой код для серверной части:
#!/usr/bin/env python3 from socket import AF_INET, socket, SOCK_STREAM from threading import Thread def accept_incoming_connections(): while True: client, client_address = SERVER.accept() print("%s:%s соединено" % client_address) client.send(bytes("Добро пожаловать , введите своё имя и нажмите Enter", "utf8")) addresses[client] = client_address Thread(target=handle_client, args=(client,)).start() def handle_client(client): name = client.recv(BUFSIZ).decode("utf8") welcome = 'Добро пожаловать %s! Если желаете выйти,то нажмите чтобы выйти.' % name client.send(bytes(welcome, "utf8")) msg = "%s вступил в переписку" % name broadcast(bytes(msg, "utf8")) clients[client] = name while True: msg = client.recv(BUFSIZ) if msg != bytes(" ", "utf8"): broadcast(msg, name+": ") else: client.send(bytes(" ", "utf8")) client.close() del clients[client] broadcast(bytes("%s покинул переписку" % name, "utf8")) break def broadcast(msg, prefix=""): for sock in clients: sock.send(bytes(prefix, "utf8")+msg) clients = <> addresses = <> HOST = '' PORT = 33000 BUFSIZ = 1024 ADDR = (HOST, PORT) SERVER = socket(AF_INET, SOCK_STREAM) SERVER.bind(ADDR) if __name__ == "__main__": SERVER.listen(5) print("ожидание соединения") ACCEPT_THREAD = Thread(target=accept_incoming_connections) ACCEPT_THREAD.start() ACCEPT_THREAD.join() SERVER.close()
Клиентская часть###
Теперь приступим к наиболее интересной части нашего приложения — к клиенту. В качестве gui будем использовать tkinter, т.к в нём довольно легко построить несложное приложение. Традиционно импортируем модуль tkinter, а также модули использовавшиеся ранее при написании серверной части программы.
#!/usr/bin/env python3 from socket import AF_INET, socket, SOCK_STREAM from threading import Thread import tkinter
Теперь мы напишем функции для обработки отправки и получения сообщений. Начнем с получения:
def receive(): """обработка получения сообщений""" while True: try: msg = client_socket.recv(BUFSIZ).decode("utf8")# декодируем,чтобы не получить кракозябры msg_list.insert(tkinter.END, msg) except OSError: break
Почему мы снова используем бесконечный цикл? Потому что мы будем получать сообщения совершенно независимо от того, как и когда мы отправляем сообщения. Мы не хотим, чтобы это было приложение для чата с функциональностью уровня рации. Мы хотим приложение в котором можно отправлять или получать сообщения одновременно; мы хотим получать сообщения, когда сами того пожелаем, и отправлять их, когда захотим.
Функциональность внутри цикла довольно проста; recv () является блокирующей частью. Он останавливает выполнение до тех пор, пока не получит сообщение, а когда это произойдет, мы продвигаемся вперед и добавляем сообщение в msglist. Затем мы определяем msg_list, который является функцией Tkinter для отображения списка сообщений на экране. Далее мы определим функцию send ():
def send(event=None): """обработка отправленных сообщений""" msg = my_msg.get() my_msg.set("") # очищаем поле. client_socket.send(bytes(msg, "utf8")) if msg == " ": client_socket.close() top.quit()
my_msg — это поле ввода в графическом интерфейсе, и поэтому мы извлекаем сообщение для отправки с помощью msg = my_msg.get (). После этого мы очищаем поле ввода и затем отправляем сообщение на сервер, который, как мы видели ранее, передает это сообщение всем клиентам (если это не сообщение о выходе). Если это сообщение о выходе, мы закрываем сокет, а затем приложение с графическим интерфейсом (через top.close ())
Мы определяем еще одну функцию, которая будет вызываться, когда мы решим закрыть окно с GUI. Это своего рода функция очистки до закрытия, которая закрывает соединение с сокетом до закрытия графического интерфейса:
def on_closing(event=None): """Эта функция вызывается когда закрывается окно""" my_msg.set(" ") send()
Это устанавливает в поле ввода значение , а затем вызывает send (). Начнем с определения виджета верхнего уровня и установки его заголовка, как и в любой другой программе на tkinter:
top = tkinter.Tk() top.title("TkMessenger")
Затем создаём фрейм со списком сообщений, поле для ввода сообщений и скроллбар для перемещения по истории переписки
messages_frame = tkinter.Frame(top) my_msg = tkinter.StringVar() my_msg.set("Введите ваше сообщение здесь.") scrollbar = tkinter.Scrollbar(messages_frame)#скроллбар
“Упаковываем” наши элементы и размечаем их расположение в окне:
msg_list = tkinter.Listbox(messages_frame, height=15, width=50, yscrollcommand=scrollbar.set) scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y) msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH) msg_list.pack() messages_frame.pack()
После этого мы создаем поле ввода для пользователя, чтобы ввести свое сообщение, и привязать его к строковой переменной, определенной выше. Мы также привязываем его к функции send (), чтобы всякий раз, когда пользователь нажимает return, сообщение отправлялось на сервер.
Далее мы создаем кнопку отправки, если пользователь желает отправить свои сообщения, нажав на нее. Опять же, мы связываем нажатие этой кнопки с функцией send ().
И да, мы также упаковываем все то, что создали только сейчас. Кроме того, не забудьте использовать функцию очистки on_closing (), которая должна вызываться, когда пользователь хочет закрыть окно GUI:
entry_field = tkinter.Entry(top, textvariable=my_msg) entry_field.bind("", send) entry_field.pack() send_button = tkinter.Button(top, text="отправить", command=send) send_button.pack() top.protocol("WM_DELETE_WINDOW", on_closing)
И вот мы подходим к завершению. Мы еще не написали код для подключения к серверу. Для этого мы должны запросить у пользователя адрес сервера. Я сделал это, просто используя input (), чтобы пользователь встретился с подсказкой командной строки, запрашивающей адрес хоста перед запуском окна с графическим интерфейсом. В будущем можно добавить виджет для этой цели. А пока вот так:
HOST = input('Введите хост: ') PORT = input('Введите порт: ') if not PORT: PORT = 33000 # Стандартный порт else: PORT = int(PORT) BUFSIZ = 1024 ADDR = (HOST, PORT) client_socket = socket(AF_INET, SOCK_STREAM) client_socket.connect(ADDR)
Как только мы получаем адрес и создаем сокет для подключения к нему, мы запускаем поток для получения сообщений, а затем основной цикл для нашего приложения с графическим интерфейсом:
receive_thread = Thread(target=receive) receive_thread.start() tkinter.mainloop()
Вот и всё! Теперь наш скрипт клиентской части выглядит вот так:
#!/usr/bin/env python3 from socket import AF_INET, socket, SOCK_STREAM from threading import Thread import tkinter def receive(): while True: try: msg = client_socket.recv(BUFSIZ).decode("utf8") msg_list.insert(tkinter.END, msg) except OSError: break def send(event=None): msg = my_msg.get() my_msg.set("") client_socket.send(bytes(msg, "utf8")) if msg == " ": client_socket.close() top.quit() def on_closing(event=None): my_msg.set(" ") send() top = tkinter.Tk() top.title("TkMessenger") messages_frame = tkinter.Frame(top) my_msg = tkinter.StringVar() my_msg.set("Введите ваше сообщение здесь") scrollbar = tkinter.Scrollbar(messages_frame) msg_list = tkinter.Listbox(messages_frame, height=15, width=50, yscrollcommand=scrollbar.set) scrollbar.pack(side=tkinter.RIGHT, fill=tkinter.Y) msg_list.pack(side=tkinter.LEFT, fill=tkinter.BOTH) msg_list.pack() messages_frame.pack() entry_field = tkinter.Entry(top, textvariable=my_msg) entry_field.bind("", send) entry_field.pack() send_button = tkinter.Button(top, text="отправить", command=send) send_button.pack() top.protocol("WM_DELETE_WINDOW", on_closing) HOST = input('Введите хост: ') PORT = input('Введите порт: ') if not PORT: PORT = 33000 else: PORT = int(PORT) BUFSIZ = 1024 ADDR = (HOST, PORT) client_socket = socket(AF_INET, SOCK_STREAM) client_socket.connect(ADDR) receive_thread = Thread(target=receive) receive_thread.start() tkinter.mainloop()
Да, наше приложение не может тягаться с такими гигантами как: telegram, viber, клиентами xmpp/jabber; однако нам удалось создать простой чат, который каждый может развить в что-то своё: сделать уклон в безопасность(например шифруя передаваемые пакеты) или в хороший ux/ui. Получилась своего рода база для чего-то большего и это круто. Спасибо за прочтение, буду рад любым замечаниям и пожеланиям. Традиционно исходный код программы доступен в моём репозитории на github.
Как создать чат-приложение на Python
Комната для чата — это интерфейс, который позволяет двум или более людям общаться в чате и отправлять сообщения всем, кто находится в комнате. Сегодня мы поговорим про то, как создать простое чат-приложение на Python и разрешить нескольким клиентам подключаться к нему с помощью сокетов.
Мы используем встроенный в Python сокет-модуль. Он дает возможность осуществлять операции с сокетами. Эти операции широко используются в Интернете: они стоят за любым подключением к любой сети.
Кроме того, для изменения цвета текста, нам понадобится пакет colorama. С помощью этого пакета мы сможем присвоить цвет каждому клиенту в нашем чате. Давайте установим этот модуль:
pip3 install colorama
Поскольку мы используем сокеты, нам нужен серверный и клиентский код. Давайте начнем с серверной части.
Серверная часть
В нашей архитектуре вся работа сервера заключается в выполнении двух основных операций:
- Прослушивание клиентских подключений. Если подключается новый клиент, мы добавляем его в нашу коллекцию клиентских сокетов
- Запуск нового потока для каждого подключенного клиента, прием сообщений, отправленных от клиента, и трансляция их всем другим пользователям.
Приведенный ниже код создает TCP-сокет и привязывает его к адресу сервера, а затем прослушивает поступающие соединения:
import socket from threading import Thread # server's IP address SERVER_HOST = "0.0.0.0" SERVER_PORT = 5002 # port we want to use separator_token # we will use this to separate the client name & message # initialize list/set of all connected client's sockets client_sockets = set() # create a TCP socket s = socket.socket() # make the port as reusable port s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # bind the socket to the address we specified s.bind((SERVER_HOST, SERVER_PORT)) # listen for upcoming connections s.listen(5) print(f"[*] Listening as :")
Обратите внимание, что мы использовали 0.0.0.0 в качестве IP-адреса сервера. Это охватывает все адреса IPv4 на локальном компьютере. Вы можете задаться вопросом, почему мы просто не используем localhost или 127.0.0.1. У сервера может быть два IP адреса, допустим 192.168.1.2 в одной сети и 10.0.0.1 в другой. При указании адреса 0.0.0.0 сервер слушает обе сети.
Мы еще не принимаем соединения, так как не вызывали метод accept() . Приведенный ниже код завершает наш бэкенд:
def listen_for_client(cs): """ This function keep listening for a message from `cs` socket Whenever a message is received, broadcast it to all other connected clients """ while True: try: # keep listening for a message from `cs` socket msg = cs.recv(1024).decode() except Exception as e: # client no longer connected # remove it from the set print(f"[!] Error: ") client_sockets.remove(cs) else: # if we received a message, replace the # token with ": " for nice printing msg = msg.replace(separator_token, ": ") # iterate over all connected sockets for client_socket in client_sockets: # and send the message client_socket.send(msg.encode()) while True: # we keep listening for new connections all the time client_socket, client_address = s.accept() print(f"[+] connected.") # add the new connected client to connected sockets client_sockets.add(client_socket) # start a new thread that listens for each client's messages t = Thread(target=listen_for_client, args=(client_socket,)) # make the thread daemon so it ends whenever the main thread ends t.daemon = True # start the thread t.start()
Как упоминалось ранее, мы добавляем подключенный клиентский сокет в коллекцию наших сокетов. Затем запускаем новый поток и устанавливаем его как поток демона (daemon thread), который выполняет определенную нами функцию listen_for_client() . При наличии клиентского сокета эта функция ожидает отправки сообщения с помощью метода recv() и затем отправляет это сообщение всем другим подключенным клиентам.
Наконец, давайте закроем все сокеты:
# close client sockets for cs in client_sockets: cs.close() # close server socket s.close()
Хорошо, с серверным кодом покончено, теперь давайте углубимся в код клиентской части.
Клиентская часть
Клиент выполняет три основные операции:
- Подключение к серверу
- Прослушивание сообщений, поступающих с сервера и вывод их на консоль (чтобы от сервера поступило сообщение, клиент должен отправить его на сервер, а сервер — распространить)
- Ожидание сообщений от пользователей для их дальнейшей отправки на сервер.
Ниже представлен код для первой операции:
import socket import random from threading import Thread from datetime import datetime from colorama import Fore, init, Back # init colors init() # set the available colors colors = [Fore.BLUE, Fore.CYAN, Fore.GREEN, Fore.LIGHTBLACK_EX, Fore.LIGHTBLUE_EX, Fore.LIGHTCYAN_EX, Fore.LIGHTGREEN_EX, Fore.LIGHTMAGENTA_EX, Fore.LIGHTRED_EX, Fore.LIGHTWHITE_EX, Fore.LIGHTYELLOW_EX, Fore.MAGENTA, Fore.RED, Fore.WHITE, Fore.YELLOW ] # choose a random color for the client client_color = random.choice(colors) # server's IP address # if the server is not on this machine, # put the private (network) IP address (e.g 192.168.1.2) SERVER_HOST = "127.0.0.1" SERVER_PORT = 5002 # server's port separator_token # we will use this to separate the client name & message # initialize TCP socket s = socket.socket() print(f"[*] Connecting to :. ") # connect to the server s.connect((SERVER_HOST, SERVER_PORT)) print("[+] Connected.")
Заодно мы устанавливаем цвет для каждого клиента (увидеть можно будет в выводе). Кроме того, давайте установим имя для каждого клиента, чтобы мы могли различать клиентов между собой:
# prompt the client for a name name = input("Enter your name: ")
Отлично, двигаемся дальше! Приведенный ниже код отвечает за вторую операцию — прослушивание сообщений с сервера и вывод их на консоль:
def listen_for_messages(): while True: message = s.recv(1024).decode() print("\n" + message) # make a thread that listens for messages to this client & print them t = Thread(target=listen_for_messages) # make the thread daemon so it ends whenever the main thread ends t.daemon = True # start the thread t.start()
Кроме того, мы хотим, чтобы прослушивание сообщений происходило в фоне, т.е. чтобы этот поток был потоком-демоном.
Переходим к последней операции — ожиданию сообщений от пользователей с последующей отправкой их на сервер. Сделаем это следующим образом:
while True: # input message we want to send to the server to_send = input() # a way to exit the program if to_send.lower() == 'q': break # add the datetime, name & the color of the sender date_now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') to_send = f"[] " # finally, send the message s.send(to_send.encode()) # close the socket s.close()
Мы добавляем цвет для каждого клиента, его имя, а также текущую дату и время к отправляемому сообщению. Дальше мы отправляем сообщение с помощью метода send() . Для выхода из программы нужно будет ввести «q» в качестве сообщения.
Демонстрация функционала
Хорошо, теперь, когда мы закончили и серверную, и клиентскую часть, давайте проверим, как всё работает. Во-первых, давайте запустим один экземпляр сервера:

Круто, сервер мониторит предстоящие подключения клиентов, давайте попробуем запустить один экземпляр клиента:

Отлично! Первый клиент подключен к серверу и ему предлагается ввести имя пользователя. Теперь, чтобы убедиться, что он подключен, вернитесь к консоли сервера:

Обратите внимание, что сейчас мы используем адрес localhost (127.0.0.1), так как это одна и та же машина. Но если вы хотите подключиться с других машин в той же сети, вы также можете это сделать, просто измените SERVER_HOST в клиентской части кода с 127.0.0.1 на частный IP-адрес сервера.
Давайте запустим еще один клиент, чтобы они могли поболтать:

Удивительно! Как видите, сообщения клиентов отличаются цветом, что позволяет различать пользователей. Что ж, давайте запустим третьего клиента для развлечения:

Заключение
Отлично, теперь каждое сообщение, отправленное одним клиентом, отправляется всем остальным. Обратите внимание, что цвета меняются при каждом повторном выполнении сценария client.py.
Пожалуйста, сверьтесь с полным кодом, чтобы вы могли легко запустить чат-приложение самостоятельно!
Мы также советуем добавить больше возможностей в эту программу. Например, можно сделать оповещение для всех пользователей при подключении нового клиента!
Итак, сегодня мы поговорили о том, как создать чат-приложение на Python. Надеемся, данная статья была вам полезна!
Успехов в написании кода!
Мессенджер на python
Появилась мысль создать простой чат\мессенджер на Python(+- 100 участников), в следствии чего возник вопрос. Мне придется искать сервер и платить за него, или можно это сделать как-то без него? А если можно без него, то как это реализовать? Попытки найти ответ на мой вопрос в интернете были, но либо я не понял, либо ответа на него не было. Если вы имеете представление как это сделать, прошу помочь, хотя бы книгой, роликом или статьей где будет про это говориться. (я не в коем случае не прошу готовое решение, я просто решил спросить у людей, которые понимают в программировании лучше меня)
Отслеживать
задан 22 июн 2021 в 22:30
193 1 1 серебряный знак 8 8 бронзовых знаков
Всё зависит от ваших целей, технически вам никто не мешает запустить свой мессенджер хоть на обычном телефоне, лишь бы ваши 100 участников могли подключиться к этому телефону
22 июн 2021 в 22:38
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Я пару раз выкладывал концепт p2p месенжера на стековерфлоу. работать можно без сервера вообще, но нужен ещё один месенжер чтоб обменяться контактами для соединения.
Реализуется это через udp и stun. stun пробивает udp порт во внешнюю сеть, а udp позволяет посылать сообщения без установки соединения = без сервера.
Нужно только опубликовать контакт (ip, port) одного из участников и через него получить остальные. Что-то в стиле DHT. Или использовать другой месенжер (например пуши гугла, публичный сип сервер или даже смски) для обмена контактами.
Если вы новичок в сетевом программировании — то начните с месенжера с сервером. Сервер можно найти бесплатно по акции или на начальных тарифах pythonanywhere, oracle и тп. На домашнем интернете есть шанс что у вас белый адрес — можно пробросить порт.
Как подружить Slack и VK с помощью Python?
5 мая 2016 г.
vetal.xp
Python для начинающих » Взаимодействие с Интернетом » Примеры Python
2

Slack обладает прекрасным потенциалом расширения стандартных возможностей. Но, на практике возникают сложности с интеграцией некоторых соц. сетей в чаты.
Для того, чтобы подружить Вконтакте и Slack мы воспользуемся Python и API. Ниже будет рассматривать базовый рецепт того, как сделать так, чтобы Slack-бот мог получать данные по последним комментариям из постов на стенах сообществ или групп Вконтакте.