57 Commits

Author SHA1 Message Date
Sergey
819b641889 Update README.md 2018-12-25 00:37:19 +03:00
Sergey
957505d53d https://vk.com/dev/version_update
https://vk.com/dev/version_update
2018-02-07 22:22:11 +03:00
Sergey
60d090aee7 Merge branch 'master' into webhook-dev 2018-02-07 22:21:20 +03:00
Kylmakalle
82afba28a2 https://vk.com/dev/version_update 2018-02-07 22:11:21 +03:00
Sergey
b9f5c3156c Update README.md 2017-10-08 19:19:52 +03:00
Kylmakalle
d6b730d699 Audio recieve support 2017-09-10 02:37:12 +03:00
Kylmakalle
022e96c09c Fixed chat chooser 2017-09-09 22:25:00 +03:00
Kylmakalle
8afba01113 fixed chat chooser 2017-09-09 22:12:38 +03:00
Kylmakalle
f1659018df Merge remote-tracking branch 'refs/remotes/origin/webhook' into webhook-dev 2017-09-09 21:51:58 +03:00
Kylmakalle
393fca6529 Revert "Merge pull request #9 from Kylmakalle/webhook"
This reverts commit 47fc72df23, reversing
changes made to ff27f0b2d7.

wrong merge reverting
2017-09-05 00:52:31 +03:00
Sergey
47fc72df23 Merge pull request #9 from Kylmakalle/webhook
Webhook updates from Master
2017-09-05 00:44:15 +03:00
Sergey
b19b94e6a9 Merge branch 'master' into webhook 2017-09-05 00:43:34 +03:00
Kylmakalle
ff27f0b2d7 Separated supervior, fix for /dialogs command 2017-08-27 15:24:09 +03:00
Kylmakalle
7bbf18375d Removed reundant escape function, tg style notify for music 2017-07-13 20:02:41 +03:00
Kylmakalle
bd09c8b9c4 Removed reundant escape function, tg style notify for music 2017-07-13 19:58:38 +03:00
Kylmakalle
41c0e36838 Fixed supervisor cycling 2017-07-13 15:21:57 +03:00
Kylmakalle
084f895897 Fixed supervisor cycling 2017-07-13 15:21:16 +03:00
Kylmakalle
7c744a8d23 Revert "Merge branch 'master' into webhook"
This reverts commit ca5e005caf, reversing
changes made to 1c5866b88d.

Reverting wrong merge
2017-07-10 15:23:33 +03:00
Sergey
256bee2b86 Update README.md 2017-07-10 01:50:16 +03:00
Sergey
70c0bdb94c Update README.md 2017-07-10 01:50:09 +03:00
Sergey
b361455e31 Support for groups, stability improvements, sending TG gifs as VK videos 2017-07-09 22:54:33 +03:00
Sergey
ca5e005caf Merge branch 'master' into webhook 2017-07-09 22:54:16 +03:00
Sergey
1c5866b88d Merge pull request #3 from Kylmakalle/webhook-dev
Webhook dev updates
2017-07-09 22:49:41 +03:00
Kylmakalle
faa580e861 Final version of stability update 2017-07-09 22:47:05 +03:00
Kylmakalle
336b84436c Exceptions logging (debug) 2017-07-09 14:48:31 +03:00
Kylmakalle
87af6c9b8d timeout fixes 2017-07-08 01:24:53 +03:00
Kylmakalle
fa57f787b0 Sending TG gifs as VK videos 2017-07-08 01:24:13 +03:00
Kylmakalle
f88ac8bfaf Support for groups and big stability improvemnts 2017-07-06 19:33:31 +03:00
Kylmakalle
25e81c46df exceptions test 2017-07-06 00:51:41 +03:00
Kylmakalle
43a617394a Stability improvemements [2] 2017-07-05 21:54:27 +03:00
Kylmakalle
c6ad06ddd7 Some stability improvements 2017-07-05 21:20:52 +03:00
Kylmakalle
d7b1011107 Moar debug 2017-07-04 22:59:37 +03:00
Kylmakalle
118f3379cb A bit more debug 2017-07-04 22:55:39 +03:00
Kylmakalle
77a2e43400 Stability improovements 2017-07-04 22:12:16 +03:00
Kylmakalle
1e2abc3018 More accurate message handler 2017-07-04 15:37:41 +03:00
Kylmakalle
2e58fd3fce More accurate message handling 2017-07-04 15:36:46 +03:00
Sergey
51fc48af4c usage link 2017-07-04 15:33:42 +03:00
Sergey
0bdcaf53a3 Update README.md 2017-07-03 20:44:10 +03:00
Sergey
76e975d626 Update README.md 2017-07-03 20:43:55 +03:00
Sergey
ba80706a7a Update README.md 2017-07-03 20:42:28 +03:00
Sergey
b1bc1e1b77 vk api timeout increase 2017-07-03 20:33:14 +03:00
Sergey
036af574f2 vk api timeout increase 2017-07-03 20:32:55 +03:00
Sergey
45d9a37f0d Audio messages Support 2017-07-03 12:42:24 +03:00
Kylmakalle
8202d215dd Telegram voices support 2017-07-03 12:40:03 +03:00
Sergey
8e2c3e993d Update bot.py 2017-07-03 03:51:54 +03:00
Sergey
1142628241 Update README.md 2017-07-02 23:45:01 +03:00
Sergey
398b1328c3 Update app.json 2017-07-02 20:03:32 +03:00
Sergey
af3f7856ad Rename requirments.txt to requirements.txt 2017-07-02 20:02:56 +03:00
Sergey
f7eb111e5b Update app.json 2017-07-02 19:58:24 +03:00
Sergey
32ddca4e4f Update app.json 2017-07-02 19:56:05 +03:00
Sergey
ca2dacc35f Update app.json 2017-07-02 19:53:49 +03:00
Sergey
73072b4e6d Create runtime.txt 2017-07-02 19:51:58 +03:00
Sergey
fabf2e8ee1 Update app.json 2017-07-02 19:47:51 +03:00
Sergey
d18bae2daa Create Procfile 2017-07-02 19:40:51 +03:00
Sergey
f81b294c18 Create requirments.txt 2017-07-02 19:40:37 +03:00
Sergey
73bbc60cf3 Update LICENSE 2017-07-02 19:25:56 +03:00
Sergey
f84a9ba455 Update LICENSE 2017-07-02 19:24:53 +03:00
35 changed files with 1330 additions and 804 deletions

17
.gitattributes vendored Normal file
View File

@@ -0,0 +1,17 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

145
.gitignore vendored Normal file
View File

@@ -0,0 +1,145 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask instance folder
instance/
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# IPython Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# dotenv
.env
# virtualenv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject
# =========================
# Operating System Files
# =========================
# OSX
# =========================
.DS_Store
.AppleDouble
.LSOverride
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
# Bot credentials
credentials.py
# PyCharm project
.idea

30
LICENSE
View File

@@ -1,16 +1,20 @@
Copyright (c) 2017 А. Сергей MIT License
Данная лицензия разрешает лицам, получившим копию данного программного обеспечения и сопутствующей документации Copyright (c) 2017 Sergey
(в дальнейшем именуемыми «Программное Обеспечение»), безвозмездно использовать Программное Обеспечение без ограничений,
включая неограниченное право на использование, копирование, изменение, слияние, публикацию,
а также лицам, которым предоставляется данное Программное Обеспечение, при соблюдении следующих условий:
Указанное выше уведомление об авторском праве и данные условия должны быть включены во все копии или значимые части Permission is hereby granted, free of charge, to any person obtaining a copy
данного Программного Обеспечения. of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, and to permit persons to whom
the Software is furnished to do so, subject to the following conditions:
ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, The above copyright notice and this permission notice shall be included in all
ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ, copies or substantial portions of the Software.
СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ.
НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
ЗА УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, В ТОМ ЧИСЛЕ, ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ СИТУАЦИИ, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
ВОЗНИКШИМ ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
Procfile Normal file
View File

@@ -0,0 +1 @@
bot: python3 bot.py

View File

@@ -1,27 +1,28 @@
# Общие функции ### Новая версия в разработке!
Актуальная ветка разработки находится [здесь](https://github.com/Kylmakalle/tgvkbot/tree/async-dev)
- [Создание и развёртывание](installation/README.md) Подробнее в [чате](https://t.me/joinchat/BZq6jwxeTh04qBzilM5x3g)
- [Использование](usage/README.md)
- [Канал в Telegram](https://t.me/tg_vk) - обновления, поддержа
Бот позволяет обмениваться сообщениями ВК используя Telegram, имеется возможность пересылки сообщений, а так же выбора диалога. # tgvkbot
<p align="center"><img src ="assets/rsz_sunset (1).jpg" /></p> Бот позволяет получать и отправлять сообщения VK находясь в Telegram
Поддерживаются различные вложения https://www.asergey.me/tgvkbot
<p align="center"><img src ="assets/documents.PNG" /></p> [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
В том числе и аудиозаписи [Ветка](https://github.com/Kylmakalle/tgvkbot/tree/webhook) для деплоя на локальной машине (поддерживаются вебхуки и лонгполлинг, закомментируйте и раскомментируйте нужные строки)
<p align="center"><img src ="assets/vk_music.jpg" /></p>
Список диалогов
<p align="center"><img src ="assets/rsz_dialogs (1).jpg" /></p>
И поиск по диалогам Настройка вебхуков по гайду от [@Groosha](https://www.gitbook.com/book/groosha/telegram-bot-lessons)
<p align="center"><img src ="assets/rsz_search (1).jpg" /></p> Бэкенд API для получения музыки через https://asergey.me/vkmusapi/
- [Создание и развёртывание](installation/README.md) https://gist.github.com/Kylmakalle/e63902025c527ac3610989530f4fa417
- [Использование](usage/README.md)
- [Канал в Telegram](https://t.me/tg_vk) - обновления, поддержа
## Stay Tuned!
олноценные комментарии к коду будут чуть позже_

View File

@@ -1,4 +0,0 @@
theme: jekyll-theme-slate
title: tgvkbot
description: 'Общайтесь ВКонтакте с помощью Telegram бота'
google_analytics: UA-101930895-1

View File

@@ -1,10 +0,0 @@
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-101930895-1', 'auto');
ga('send', 'pageview');
</script>

View File

@@ -1,425 +0,0 @@
@import "rouge-github";
/*******************************************************************************
MeyerWeb Reset
*******************************************************************************/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
ol, ul {
list-style: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/*******************************************************************************
Theme Styles
*******************************************************************************/
body {
box-sizing: border-box;
color:#373737;
background: #212121;
font-size: 20px;
font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 {
margin: 10px 0;
font-weight: 700;
color:#222222;
font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif;
letter-spacing: -1px;
}
h1 {
font-size: 36px;
font-weight: 700;
}
h2 {
padding-bottom: 10px;
font-size: 32px;
background: url('../images/bg_hr.png') repeat-x bottom;
}
h3 {
font-size: 24px;
}
h4 {
font-size: 21px;
}
h5 {
font-size: 18px;
}
h6 {
font-size: 16px;
}
p {
margin: 10px 0 15px 0;
}
footer p {
color: #f2f2f2;
}
a {
text-decoration: none;
color: #0F79D0;
text-shadow: none;
transition: color 0.5s ease;
transition: text-shadow 0.5s ease;
-webkit-transition: color 0.5s ease;
-webkit-transition: text-shadow 0.5s ease;
-moz-transition: color 0.5s ease;
-moz-transition: text-shadow 0.5s ease;
-o-transition: color 0.5s ease;
-o-transition: text-shadow 0.5s ease;
-ms-transition: color 0.5s ease;
-ms-transition: text-shadow 0.5s ease;
}
a:hover, a:focus {
text-decoration: underline;
font-weight:bold;
}
footer a {
color: #F2F2F2;
text-decoration: underline;
}
em {
font-style: italic;
}
strong {
font-weight: bold;
}
img {
position: relative;
margin: 0 auto;
max-width: 739px;
padding: 5px;
margin: 10px 0 10px 0;
border: 1px solid #ebebeb;
box-shadow: 0 0 5px #ebebeb;
-webkit-box-shadow: 0 0 5px #ebebeb;
-moz-box-shadow: 0 0 5px #ebebeb;
-o-box-shadow: 0 0 5px #ebebeb;
-ms-box-shadow: 0 0 5px #ebebeb;
}
p img {
display: inline;
margin: 0;
padding: 0;
vertical-align: middle;
text-align: center;
border: none;
}
pre, code {
color: #222;
background-color: #fff;
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
font-size: 14px;
border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
}
pre {
padding: 10px;
box-shadow: 0 0 10px rgba(0,0,0,.1);
overflow: auto;
}
code {
padding: 3px;
margin: 0 3px;
box-shadow: 0 0 10px rgba(0,0,0,.1);
}
pre code {
display: block;
box-shadow: none;
}
blockquote {
color: #666;
margin-bottom: 20px;
padding: 0 0 0 20px;
border-left: 3px solid #bbb;
}
ul, ol, dl {
margin-bottom: 15px
}
ul {
list-style-position: inside;
list-style: disc;
padding-left: 20px;
}
ol {
list-style-position: inside;
list-style: decimal;
padding-left: 20px;
}
dl dt {
font-weight: bold;
}
dl dd {
padding-left: 20px;
font-style: italic;
}
dl p {
padding-left: 20px;
font-style: italic;
}
hr {
height: 1px;
margin-bottom: 5px;
border: none;
background: url('../images/bg_hr.png') repeat-x center;
}
table {
border: 1px solid #373737;
margin-bottom: 20px;
text-align: left;
}
th {
font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif;
padding: 10px;
background: #373737;
color: #fff;
}
td {
padding: 10px;
border: 1px solid #373737;
}
form {
background: #f2f2f2;
padding: 20px;
}
/*******************************************************************************
Full-Width Styles
*******************************************************************************/
.outer {
width: 100%;
}
.inner {
position: relative;
max-width: 640px;
padding: 20px 10px;
margin: 0 auto;
}
#forkme_banner {
display: block;
position: absolute;
top:0;
right: 10px;
z-index: 10;
padding: 10px 50px 10px 10px;
color: #fff;
background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%;
font-weight: 700;
box-shadow: 0 0 10px rgba(0,0,0,.5);
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
#header_wrap {
background: #212121;
background: -moz-linear-gradient(top, #373737, #212121);
background: -webkit-linear-gradient(top, #373737, #212121);
background: -ms-linear-gradient(top, #373737, #212121);
background: -o-linear-gradient(top, #373737, #212121);
background: linear-gradient(top, #373737, #212121);
}
#header_wrap .inner {
padding: 50px 10px 30px 10px;
}
#project_title {
margin: 0;
color: #fff;
font-size: 42px;
font-weight: 700;
text-shadow: #111 0px 0px 10px;
}
#project_tagline {
color: #fff;
font-size: 24px;
font-weight: 300;
background: none;
text-shadow: #111 0px 0px 10px;
}
#downloads {
position: absolute;
width: 210px;
z-index: 10;
bottom: -40px;
right: 0;
height: 70px;
background: url('../images/icon_download.png') no-repeat 0% 90%;
}
.zip_download_link {
display: block;
float: right;
width: 90px;
height:70px;
text-indent: -5000px;
overflow: hidden;
background: url(../images/sprite_download.png) no-repeat bottom left;
}
.tar_download_link {
display: block;
float: right;
width: 90px;
height:70px;
text-indent: -5000px;
overflow: hidden;
background: url(../images/sprite_download.png) no-repeat bottom right;
margin-left: 10px;
}
.zip_download_link:hover {
background: url(../images/sprite_download.png) no-repeat top left;
}
.tar_download_link:hover {
background: url(../images/sprite_download.png) no-repeat top right;
}
#main_content_wrap {
background: #f2f2f2;
border-top: 1px solid #111;
border-bottom: 1px solid #111;
}
#main_content {
padding-top: 40px;
}
#footer_wrap {
background: #212121;
}
/*******************************************************************************
Small Device Styles
*******************************************************************************/
@media screen and (max-width: 992px) {
img {
max-width: 100%;
}
}
@media screen and (max-width: 480px) {
body {
font-size:14px;
}
#downloads {
display: none;
}
.inner {
min-width: 300px;
max-width: 480px;
}
#project_title {
font-size: 32px;
}
h1 {
font-size: 28px;
}
h2 {
font-size: 24px;
}
h3 {
font-size: 21px;
}
h4 {
font-size: 18px;
}
h5 {
font-size: 14px;
}
h6 {
font-size: 12px;
}
code, pre {
font-size: 11px;
}
}

View File

@@ -1,209 +0,0 @@
.highlight table td { padding: 5px; }
.highlight table pre { margin: 0; }
.highlight .cm {
color: #777772;
font-style: italic;
}
.highlight .cp {
color: #797676;
font-weight: bold;
}
.highlight .c1 {
color: #777772;
font-style: italic;
}
.highlight .cs {
color: #797676;
font-weight: bold;
font-style: italic;
}
.highlight .c, .highlight .cd {
color: #777772;
font-style: italic;
}
.highlight .err {
color: #a61717;
background-color: #e3d2d2;
}
.highlight .gd {
color: #000000;
background-color: #ffdddd;
}
.highlight .ge {
color: #000000;
font-style: italic;
}
.highlight .gr {
color: #aa0000;
}
.highlight .gh {
color: #797676;
}
.highlight .gi {
color: #000000;
background-color: #ddffdd;
}
.highlight .go {
color: #888888;
}
.highlight .gp {
color: #555555;
}
.highlight .gs {
font-weight: bold;
}
.highlight .gu {
color: #aaaaaa;
}
.highlight .gt {
color: #aa0000;
}
.highlight .kc {
color: #000000;
font-weight: bold;
}
.highlight .kd {
color: #000000;
font-weight: bold;
}
.highlight .kn {
color: #000000;
font-weight: bold;
}
.highlight .kp {
color: #000000;
font-weight: bold;
}
.highlight .kr {
color: #000000;
font-weight: bold;
}
.highlight .kt {
color: #445588;
font-weight: bold;
}
.highlight .k, .highlight .kv {
color: #000000;
font-weight: bold;
}
.highlight .mf {
color: #009999;
}
.highlight .mh {
color: #009999;
}
.highlight .il {
color: #009999;
}
.highlight .mi {
color: #009999;
}
.highlight .mo {
color: #009999;
}
.highlight .m, .highlight .mb, .highlight .mx {
color: #009999;
}
.highlight .sb {
color: #d14;
}
.highlight .sc {
color: #d14;
}
.highlight .sd {
color: #d14;
}
.highlight .s2 {
color: #d14;
}
.highlight .se {
color: #d14;
}
.highlight .sh {
color: #d14;
}
.highlight .si {
color: #d14;
}
.highlight .sx {
color: #d14;
}
.highlight .sr {
color: #009926;
}
.highlight .s1 {
color: #d14;
}
.highlight .ss {
color: #990073;
}
.highlight .s {
color: #d14;
}
.highlight .na {
color: #008080;
}
.highlight .bp {
color: #797676;
}
.highlight .nb {
color: #0086B3;
}
.highlight .nc {
color: #445588;
font-weight: bold;
}
.highlight .no {
color: #008080;
}
.highlight .nd {
color: #3c5d5d;
font-weight: bold;
}
.highlight .ni {
color: #800080;
}
.highlight .ne {
color: #990000;
font-weight: bold;
}
.highlight .nf {
color: #990000;
font-weight: bold;
}
.highlight .nl {
color: #990000;
font-weight: bold;
}
.highlight .nn {
color: #555555;
}
.highlight .nt {
color: #000080;
}
.highlight .vc {
color: #008080;
}
.highlight .vg {
color: #008080;
}
.highlight .vi {
color: #008080;
}
.highlight .nv {
color: #008080;
}
.highlight .ow {
color: #000000;
font-weight: bold;
}
.highlight .o {
color: #000000;
font-weight: bold;
}
.highlight .w {
color: #bbbbbb;
}
.highlight {
background-color: #f8f8f8;
}

34
app.json Normal file
View File

@@ -0,0 +1,34 @@
{
"name": "tgvkbot",
"description": "Бот позволяет получать и отправлять сообщения VK находясь в Telegram",
"repository": "https://github.com/Kylmakalle/tgvkbot",
"keywords": ["vk", "bot", "telegram"],
"website": "https://asergey.me/tgvkbot/",
"buildpacks":[
{
"url": "heroku/python"
}
],
"env": {
"TELEGRAM_TOKEN": {
"description": "Telegram bot API токен от https://t.me/BotFather",
"value": "123456789:AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLL"
},
"VK_APP": {
"description": "ID VK приложения из https://vk.com/apps?act=manage",
"required": true
}
},
"addons": [
{
"plan": "heroku-redis:hobby-dev",
"as": "Redis"
}
],
"formation": {
"bot": {
"quantity": 1,
"size": "free"
}
}
}

View File

@@ -1,5 +0,0 @@
---
---
@import "jekyll-theme-slate";
@import "style.css";

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -1 +0,0 @@

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

721
bot.py Normal file
View File

@@ -0,0 +1,721 @@
import os
import re
import redis
import requests
import telebot
import threading
import time
import traceback
import ujson
import vk
import wget
from PIL import Image
from telebot import types
from credentials import token, vk_app_id
from vk_messages import VkMessage, VkPolling
vk_threads = {}
vk_dialogs = {}
VK_API_VERSION = '3.0'
FILE_URL = 'https://api.telegram.org/file/bot{0}/{1}'
vk_tokens = redis.from_url(os.environ.get("REDIS_URL"))
currentchat = {}
bot = telebot.AsyncTeleBot(token)
bot.remove_webhook()
link = 'https://oauth.vk.com/authorize?client_id={}&' \
'display=page&redirect_uri=https://oauth.vk.com/blank.html&scope=friends,messages,offline,docs,photos,video' \
'&response_type=token&v={}'.format(vk_app_id, VK_API_VERSION)
def get_pages_switcher(markup, page, pages):
if page != 0:
leftbutton = types.InlineKeyboardButton('', callback_data='page{}'.format(page - 1)) # callback
else:
leftbutton = types.InlineKeyboardButton('Поиск 🔍', callback_data='search')
if page + 1 < len(pages):
rightbutton = types.InlineKeyboardButton('', callback_data='page{}'.format(page + 1))
else:
rightbutton = None
if rightbutton:
markup.row(leftbutton, rightbutton)
else:
markup.row(leftbutton)
def replace_shields(text):
text = text.replace('&lt;', '<')
text = text.replace('&gt;', '>')
text = text.replace('&amp;', '&')
text = text.replace('&copy;', '©')
text = text.replace('&reg;', '®')
text = text.replace('&laquo;', '«')
text = text.replace('&raquo;', '«')
text = text.replace('&deg;', '°')
text = text.replace('&trade;', '')
text = text.replace('&plusmn;', '±')
return text
def request_user_dialogs(session, userid):
order = []
users_ids = []
group_ids = []
positive_group_ids = []
api = vk.API(session, v=VK_API_VERSION)
dialogs = api.messages.getDialogs(count=200)
for chat in dialogs[1:]:
if 'chat_id' in chat:
chat['title'] = replace_shields(chat['title'])
order.append({'title': chat['title'], 'id': 'group' + str(chat['chat_id'])})
elif chat['uid'] > 0:
order.append({'title': None, 'id': chat['uid']})
users_ids.append(chat['uid'])
elif chat['uid'] < 0:
order.append({'title': None, 'id': chat['uid']})
group_ids.append(chat['uid'])
for g in group_ids:
positive_group_ids.append(str(g)[1:])
if users_ids:
users = api.users.get(user_ids=users_ids, fields=['first_name', 'last_name', 'uid'])
else:
users = []
if positive_group_ids:
groups = api.groups.getById(group_ids=positive_group_ids, fields=[])
else:
groups = []
for output in order:
if output['title'] == ' ... ' or not output['title']:
if output['id'] > 0:
for x in users:
if x['uid'] == output['id']:
output['title'] = '{} {}'.format(x['first_name'], x['last_name'])
break
else:
for f in groups:
if str(f['gid']) == str(output['id'])[1:]:
output['title'] = '{}'.format(f['name'])
break
for button in range(len(order)):
order[button] = types.InlineKeyboardButton(order[button]['title'], callback_data=str(order[button]['id']))
rows = [order[x:x + 2] for x in range(0, len(order), 2)]
pages = [rows[x:x + 4] for x in range(0, len(rows), 4)]
vk_dialogs[str(userid)] = pages
def create_markup(message, user, page, edit=False):
markup = types.InlineKeyboardMarkup(row_width=2)
for i in vk_dialogs[str(user)][page]:
markup.row(*i)
get_pages_switcher(markup, page, vk_dialogs[str(user)])
if edit:
bot.edit_message_text(
'<b>Выберите Диалог:</b> <code>{}/{}</code> стр.'.format(page + 1, len(vk_dialogs[str(user)])),
message.chat.id, message.message_id,
parse_mode='HTML', reply_markup=markup).wait()
else:
bot.send_message(message.from_user.id,
'<b>Выберите Диалог:</b> <code>{}/{}</code> стр.'.format(page + 1, len(vk_dialogs[str(user)])),
parse_mode='HTML', reply_markup=markup).wait()
def search_users(message, text):
session = VkMessage(vk_tokens.get(str(message.from_user.id))).session
api = vk.API(session, v=VK_API_VERSION)
markup = types.InlineKeyboardMarkup(row_width=1)
result = api.messages.searchDialogs(q=text, limit=10, fields=[])
for chat in result:
if chat['type'] == 'profile':
markup.add(types.InlineKeyboardButton('{} {}'.format(chat['first_name'], chat['last_name']),
callback_data=str(chat['uid'])))
elif chat['type'] == 'chat':
markup.add(
types.InlineKeyboardButton(replace_shields(chat['title']),
callback_data='group' + str(chat['chat_id'])))
if markup.keyboard:
markup.add(types.InlineKeyboardButton('Поиск 🔍', callback_data='search'))
bot.send_message(message.from_user.id, '<b>Результат поиска по</b> <i>{}</i>'.format(text),
reply_markup=markup, parse_mode='HTML')
else:
markup.add(types.InlineKeyboardButton('Поиск 🔍', callback_data='search'))
bot.send_message(message.from_user.id, '<b>Ничего не найдено по запросу</b> <i>{}</i>'.format(text),
parse_mode='HTML', reply_markup=markup)
@bot.callback_query_handler(func=lambda call: True)
def callback_buttons(call):
if call.message:
if 'page' in call.data:
try:
create_markup(call.message, call.from_user.id, int(call.data.split('page')[1]), True)
except:
session = VkMessage(vk_tokens.get(str(call.from_user.id))).session
request_user_dialogs(session, call.from_user.id)
create_markup(call.message, call.from_user.id, int(call.data.split('page')[1]), True)
bot.answer_callback_query(call.id).wait()
elif 'search' in call.data:
markup = types.ForceReply(selective=False)
bot.answer_callback_query(call.id, 'Поиск беседы 🔍').wait()
bot.send_message(call.from_user.id, '<b>Поиск беседы</b> 🔍',
parse_mode='HTML', reply_markup=markup).wait()
elif 'group' in call.data:
session = VkMessage(vk_tokens.get(str(call.from_user.id))).session
api = vk.API(session, v=VK_API_VERSION)
chat = api.messages.getChat(chat_id=call.data.split('group')[1], fields=[])
bot.answer_callback_query(call.id,
'Вы в беседе {}'.format(replace_shields(chat['title']))).wait()
bot.send_message(call.from_user.id,
'<i>Вы в беседе {}</i>'.format(chat['title']),
parse_mode='HTML').wait()
currentchat[str(call.from_user.id)] = {'title': chat['title'], 'id': 'group' + str(chat['chat_id'])}
elif call.data.lstrip('-').isdigit():
session = VkMessage(vk_tokens.get(str(call.from_user.id))).session
api = vk.API(session, v=VK_API_VERSION)
if '-' in call.data:
user = api.groups.getById(group_id=call.data.lstrip('-'), fields=[])[0]
user = {'first_name': user['name'], 'last_name': ''}
else:
user = api.users.get(user_ids=call.data, fields=[])[0]
bot.answer_callback_query(call.id,
'Вы в чате с {} {}'.format(user['first_name'], user['last_name'])).wait()
bot.send_message(call.from_user.id,
'<i>Вы в чате с {} {}</i>'.format(user['first_name'], user['last_name']),
parse_mode='HTML').wait()
currentchat[str(call.from_user.id)] = {'title': user['first_name'] + ' ' + user['last_name'],
'id': call.data}
def create_thread(uid, vk_token):
a = VkPolling()
longpoller = VkMessage(vk_token)
t = threading.Thread(name='vk' + str(uid), target=a.run, args=(longpoller, bot, uid,))
t.setDaemon(True)
t.start()
vk_threads[str(uid)] = a
vk_tokens.set(str(uid), vk_token)
session = longpoller.session
api = vk.API(session, v=VK_API_VERSION)
api.account.setOffline()
def check_thread(uid):
for th in threading.enumerate():
if th.getName() == 'vk' + str(uid):
return False
return True
# Creating VkPolling threads and dialogs info after bot's reboot/exception using existing tokens
def thread_reviver(uid):
tries = 0
while check_thread(uid.decode("utf-8")):
if tries < 4:
try:
create_thread(uid.decode("utf-8"), vk_tokens.get(uid))
except:
time.sleep(10)
tries = tries + 1
else:
mark = types.InlineKeyboardMarkup()
login = types.InlineKeyboardButton('ВХОД', url=link)
mark.add(login)
bot.send_message(uid.decode("utf-8"), '<b>Непредвиденная ошибка, требуется повторный логин ВК!</b>',
parse_mode='HTML', reply_markup=mark).wait()
break
def thread_supervisor():
while True:
for uid in vk_tokens.scan_iter():
reviver_thread = threading.Thread(name='reviver' + str(uid.decode('utf-8')), target=thread_reviver,
args=(uid,))
reviver_thread.setDaemon(True)
reviver_thread.start()
time.sleep(60)
supervisor = threading.Thread(name='supervisor', target=thread_supervisor)
supervisor.setDaemon(True)
supervisor.start()
def stop_thread(message):
for th in threading.enumerate():
if th.getName() == 'vk' + str(message.from_user.id):
t = vk_threads[str(message.from_user.id)]
t.terminate()
th.join()
vk_tokens.delete(str(message.from_user.id))
vk_dialogs.pop(str(message.from_user.id), None)
currentchat.pop(str(message.from_user.id), None)
def extract_unique_code(text):
# Extracts the unique_code from the sent /start command.
try:
return text[45:].split('&')[0]
except:
return None
def verifycode(code):
session = vk.Session(access_token=code)
api = vk.API(session, v=VK_API_VERSION)
return dict(api.account.getProfileInfo(fields=[]))
def info_extractor(info):
info = info[-1].url[8:-1].split('.')
return info
@bot.message_handler(commands=['chat'])
def chat_command(message):
if logged(message):
if str(message.from_user.id) in currentchat:
if 'group' in currentchat[str(message.from_user.id)]['id']:
chat = currentchat[str(message.from_user.id)]
bot.send_message(message.from_user.id,
'<i>Вы в беседе {}</i>'.format(chat['title']),
parse_mode='HTML').wait()
else:
chat = currentchat[str(message.from_user.id)]
bot.send_message(message.from_user.id,
'<i>Вы в чате с {}</i>'.format(chat['title']),
parse_mode='HTML').wait()
else:
bot.send_message(message.from_user.id,
'<i>Вы не находитесь в чате</i>',
parse_mode='HTML').wait()
@bot.message_handler(commands=['leave'])
def leave_command(message):
if logged(message):
if str(message.from_user.id) in currentchat:
currentchat.pop(str(message.from_user.id), None)
bot.send_message(message.from_user.id,
'<i>Вы вышли из чата</i>',
parse_mode='HTML').wait()
else:
bot.send_message(message.from_user.id,
'<i>Вы не находитесь в чате</i>',
parse_mode='HTML').wait()
@bot.message_handler(commands=['dialogs'])
def dialogs_command(message):
if logged(message):
session = VkMessage(vk_tokens.get(str(message.from_user.id))).session
request_user_dialogs(session, message.from_user.id)
create_markup(message, message.from_user.id, 0)
@bot.message_handler(commands=['search'])
def search_command(message):
if logged(message):
markup = types.ForceReply(selective=False)
if telebot.util.extract_arguments(message.text):
search_users(message, telebot.util.extract_arguments(message.text))
else:
bot.send_message(message.from_user.id, '<b>Поиск беседы</b> 🔍',
parse_mode='HTML', reply_markup=markup).wait()
@bot.message_handler(commands=['stop'])
def stop_command(message):
if not check_thread(message.from_user.id):
stop_thread(message)
bot.send_message(message.from_user.id, 'Успешный выход!').wait()
else:
bot.send_message(message.from_user.id, 'Вход не был выполнен!').wait()
@bot.message_handler(commands=['start'])
def start_command(message):
if check_thread(message.from_user.id):
mark = types.InlineKeyboardMarkup()
login = types.InlineKeyboardButton('ВХОД', url=link)
mark.add(login)
bot.send_message(message.from_user.id,
'Привет, этот бот поможет тебе общаться ВКонтакте, войди по кнопке ниже'
' и отправь мне то, что получишь в адресной строке.',
reply_markup=mark).wait()
else:
bot.send_message(message.from_user.id, 'Вход уже выполнен!\n/stop для выхода.').wait()
def form_request(message, method, info):
if int(info[2]):
if message.text and message.text.startswith('!'):
if len(message.text) - 1:
message.text = message.text[1:]
if info[2] != 'None':
method(message, info[1], group=True, forward_messages=info[2])
else:
method(message, info[1], group=True)
elif message.caption and message.caption.startswith('!'):
if len(message.caption) - 1:
message.caption = message.caption[1:]
if info[2] != 'None':
method(message, info[1], group=True, forward_messages=info[2])
else:
method(message, info[1], group=True)
else:
if message.text and message.text.startswith('!'):
if len(message.text) - 1:
message.text = message.text[1:]
if info[1] != 'None':
method(message, info[0], group=False, forward_messages=info[1])
else:
method(message, info[0], group=False)
elif message.caption and message.caption.startswith('!'):
if len(message.caption) - 1:
message.caption = message.caption[1:]
if info[1] != 'None':
method(message, info[0], group=False, forward_messages=info[1])
else:
method(message, info[0], group=False)
else:
method(message, info[0], group=False)
def logged(message):
if vk_tokens.get(str(message.from_user.id)):
return True
else:
bot.send_message(message.from_user.id, 'Вход не выполнен! /start для входа').wait()
return False
def vk_sender(message, method):
if logged(message):
if message.reply_to_message:
info = info_extractor(message.reply_to_message.entities)
if info is not None:
form_request(message, method, info)
elif str(message.from_user.id) in currentchat:
info = []
if 'group' in currentchat[str(message.from_user.id)]['id']:
info.append('0')
info.append(currentchat[str(message.from_user.id)]['id'].split('group')[1])
info.append('1')
else:
info.append(currentchat[str(message.from_user.id)]['id'])
info.append('0')
info.append('0')
form_request(message, method, info)
def audio_title_creator(message, performer=None, title=None):
if not performer and not title:
return 'Аудио_{}'.format(str(message.date)[5:])
else:
return '{} - {}'.format(performer, title)
def send_text(message, userid, group, forward_messages=None):
session = VkMessage(vk_tokens.get(str(message.from_user.id))).session
api = vk.API(session, v=VK_API_VERSION)
if group:
api.messages.send(chat_id=userid, message=message.text, forward_messages=forward_messages)
else:
api.messages.send(user_id=userid, message=message.text, forward_messages=forward_messages)
def send_doc(message, userid, group, forward_messages=None):
filetype = message.content_type
session = VkMessage(vk_tokens.get(str(message.from_user.id))).session
api = vk.API(session, v=VK_API_VERSION)
if filetype == 'document' and 'video' not in message.document.mime_type:
file = wget.download(
FILE_URL.format(token, bot.get_file(getattr(message, filetype).file_id).wait().file_path))
openedfile = open(file, 'rb')
files = {'file': openedfile}
fileonserver = ujson.loads(requests.post(api.docs.getUploadServer()['upload_url'],
files=files).text)
attachment = api.docs.save(file=fileonserver['file'],
title=getattr(message, filetype).file_name,
tags='')
openedfile.close()
os.remove(file)
elif filetype == 'voice':
file = wget.download(
FILE_URL.format(token, bot.get_file(getattr(message, filetype).file_id).wait().file_path))
openedfile = open(file, 'rb')
files = {'file': openedfile}
fileonserver = ujson.loads(
requests.post(api.docs.getUploadServer(type='audio_message')['upload_url'],
files=files).text)
attachment = api.docs.save(file=fileonserver['file'], title='Аудиосообщение',
tags='')
openedfile.close()
os.remove(file)
elif filetype == 'document' and 'video' in message.document.mime_type:
vk_sender(message, send_video)
return
else: # filetype == 'audio':
file = wget.download(
FILE_URL.format(token, bot.get_file(getattr(message, filetype).file_id).wait().file_path))
newfile = file.split('.')[0] + '.aac'
os.rename(file, newfile)
openedfile = open(newfile, 'rb')
files = {'file': openedfile}
fileonserver = ujson.loads(requests.post(api.docs.getUploadServer()['upload_url'],
files=files).text)
attachment = api.docs.save(file=fileonserver['file'],
title=audio_title_creator(message, message.audio.performer,
message.audio.title), tags='')
openedfile.close()
os.remove(newfile)
if group:
if message.caption:
api.messages.send(chat_id=userid, message=message.caption,
attachment='doc{}_{}'.format(attachment[0]['owner_id'],
attachment[0]['did']),
forward_messages=forward_messages)
else:
api.messages.send(chat_id=userid,
attachment='doc{}_{}'.format(attachment[0]['owner_id'],
attachment[0]['did']),
forward_messages=forward_messages)
else:
if message.caption:
api.messages.send(user_id=userid, message=message.caption,
attachment='doc{}_{}'.format(attachment[0]['owner_id'],
attachment[0]['did']),
forward_messages=forward_messages)
else:
api.messages.send(user_id=userid,
attachment='doc{}_{}'.format(attachment[0]['owner_id'],
attachment[0]['did']),
forward_messages=forward_messages)
def send_photo(message, userid, group, forward_messages=None):
filetype = message.content_type
session = VkMessage(vk_tokens.get(str(message.from_user.id))).session
api = vk.API(session, v=VK_API_VERSION)
file = wget.download(
FILE_URL.format(token, bot.get_file(getattr(message, filetype)[-1].file_id).wait().file_path))
openedfile = open(file, 'rb')
files = {'file': openedfile}
fileonserver = ujson.loads(requests.post(api.photos.getMessagesUploadServer()['upload_url'],
files=files).text)
attachment = api.photos.saveMessagesPhoto(server=fileonserver['server'], photo=fileonserver['photo'],
hash=fileonserver['hash'])
if group:
if message.caption:
api.messages.send(chat_id=userid, message=message.caption, attachment=attachment[0]['id'],
forward_messages=forward_messages)
else:
api.messages.send(chat_id=userid, attachment=attachment[0]['id'],
forward_messages=forward_messages)
else:
if message.caption:
api.messages.send(user_id=userid, message=message.caption, attachment=attachment[0]['id'],
forward_messages=forward_messages)
else:
api.messages.send(user_id=userid, attachment=attachment[0]['id'],
forward_messages=forward_messages)
openedfile.close()
os.remove(file)
def send_sticker(message, userid, group, forward_messages=None):
filetype = message.content_type
session = VkMessage(vk_tokens.get(str(message.from_user.id))).session
api = vk.API(session, v=VK_API_VERSION)
file = wget.download(
FILE_URL.format(token, bot.get_file(getattr(message, filetype).file_id).wait().file_path))
Image.open(file).save("{}.png".format(file))
openedfile = open('{}.png'.format(file), 'rb')
files = {'file': openedfile}
fileonserver = ujson.loads(requests.post(api.photos.getMessagesUploadServer()['upload_url'],
files=files).text)
attachment = api.photos.saveMessagesPhoto(server=fileonserver['server'], photo=fileonserver['photo'],
hash=fileonserver['hash'])
if group:
if message.caption:
api.messages.send(chat_id=userid, message=message.caption, attachment=attachment[0]['id'],
forward_messages=forward_messages)
else:
api.messages.send(chat_id=userid, attachment=attachment[0]['id'],
forward_messages=forward_messages)
else:
if message.caption:
api.messages.send(user_id=userid, message=message.caption, attachment=attachment[0]['id'],
forward_messages=forward_messages)
else:
api.messages.send(user_id=userid, attachment=attachment[0]['id'],
forward_messages=forward_messages)
openedfile.close()
os.remove('{}.png'.format(file))
os.remove(file)
def send_video(message, userid, group, forward_messages=None):
filetype = message.content_type
session = VkMessage(vk_tokens.get(str(message.from_user.id))).session
api = vk.API(session, v=VK_API_VERSION)
file = wget.download(
FILE_URL.format(token, bot.get_file(getattr(message, filetype).file_id).wait().file_path))
openedfile = open(file, 'rb')
files = {'video_file': openedfile}
if group:
attachment = api.video.save(is_private=1)
fileonserver = ujson.loads(requests.post(attachment['upload_url'],
files=files).text)
video = 'video{}_{}'.format(attachment['owner_id'], attachment['owner_id']['video_id'])
if message.caption:
api.messages.send(chat_id=userid, message=message.caption, attachment=video,
forward_messages=forward_messages)
else:
api.messages.send(chat_id=userid, attachment=video, forward_messages=forward_messages)
else:
attachment = api.video.save(is_private=1)
fileonserver = ujson.loads(requests.post(attachment['upload_url'],
files=files).text)
video = 'video{}_{}'.format(attachment['owner_id'], attachment['vid'])
if message.caption:
api.messages.send(user_id=userid, message=message.caption, attachment=video,
forward_messages=forward_messages)
else:
api.messages.send(user_id=userid, attachment=video, forward_messages=forward_messages)
openedfile.close()
os.remove(file)
def send_contact(message, userid, group, forward_messages=None):
session = VkMessage(vk_tokens.get(str(message.from_user.id))).session
api = vk.API(session, v=VK_API_VERSION)
if message.contact.last_name:
text = 'Контакт: {} {}'.format(message.contact.first_name, message.contact.last_name)
else:
text = 'Контакт: {}'.format(message.contact.first_name)
if group:
api.messages.send(chat_id=userid, message=text, forward_messages=forward_messages)
api.messages.send(chat_id=userid, message=message.contact, forward_messages=forward_messages)
else:
api.messages.send(user_id=userid, message=text, forward_messages=forward_messages)
api.messages.send(chat_id=userid, message=message.contact, forward_messages=forward_messages)
@bot.message_handler(content_types=['document', 'voice', 'audio'])
def reply_document(message):
try:
vk_sender(message, send_doc)
except:
bot.reply_to(message, 'Файл слишком большой, максимально допустимый размер *20мб*!',
parse_mode='Markdown').wait()
@bot.message_handler(content_types=['sticker'])
def reply_sticker(message):
try:
vk_sender(message, send_sticker)
except Exception:
bot.reply_to(message, '*Произошла неизвестная ошибка при отправке*',
parse_mode='Markdown').wait() # TODO?: Bugreport system
print('Error: {}'.format(traceback.format_exc()))
@bot.message_handler(content_types=['photo'])
def reply_photo(message):
try:
vk_sender(message, send_photo)
except:
bot.send_message(message.from_user.id, 'Фото слишком большое, максимально допустимый размер *20мб*!',
parse_mode='Markdown').wait()
@bot.message_handler(content_types=['video', 'video_note'])
def reply_video(message):
try:
vk_sender(message, send_video)
except:
bot.reply_to(message, 'Файл слишком большой, максимально допустимый размер *20мб*!',
parse_mode='Markdown').wait()
@bot.message_handler(content_types=['contact'])
def reply_contact(message):
try:
vk_sender(message, send_contact)
except Exception:
bot.reply_to(message, '*Произошла неизвестная ошибка при отправке*',
parse_mode='Markdown').wait()
print('Error: {}'.format(traceback.format_exc()))
@bot.message_handler(content_types=['text'])
def reply_text(message):
m = re.search('https://oauth\.vk\.com/blank\.html#access_token=[a-z0-9]*&expires_in=[0-9]*&user_id=[0-9]*',
message.text)
if m:
code = extract_unique_code(m.group(0))
if check_thread(message.from_user.id):
try:
user = verifycode(code)
create_thread(message.from_user.id, code)
bot.send_message(message.from_user.id,
'Вход выполнен в аккаунт {} {}!'.format(user['first_name'], user['last_name'])).wait()
bot.send_message(message.from_user.id, '[Использование](https://asergey.me/tgvkbot/usage/)',
parse_mode='Markdown').wait()
except:
bot.send_message(message.from_user.id, 'Неверная ссылка, попробуй ещё раз!').wait()
else:
bot.send_message(message.from_user.id, 'Вход уже выполнен!\n/stop для выхода.').wait()
elif message.reply_to_message and message.reply_to_message.text == 'Поиск беседы 🔍':
search_users(message, message.text)
else:
try:
vk_sender(message, send_text)
except Exception:
bot.reply_to(message, 'Произошла неизвестная ошибка при отправке',
parse_mode='Markdown').wait()
bot.polling(none_stop=True)
"""class WebhookServer(object):
# index равнозначно /, т.к. отсутствию части после ip-адреса (грубо говоря)
@cherrypy.expose
def index(self):
length = int(cherrypy.request.headers['content-length'])
json_string = cherrypy.request.body.read(length).decode("utf-8")
update = telebot.types.Update.de_json(json_string)
bot.process_new_updates([update])
return ''
if __name__ == '__main__':
logging.basicConfig(format='%(levelname)-8s [%(asctime)s] %(message)s', level=logging.WARNING, filename='vk.log')
bot.remove_webhook()
bot.set_webhook('https://{}/{}/'.format(bot_url, token))
cherrypy.config.update(
{'server.socket_host': '127.0.0.1', 'server.socket_port': local_port, 'engine.autoreload.on': False,
'log.screen': False})
cherrypy.quickstart(WebhookServer(), '/', {'/': {}})"""

3
credentials.py Normal file
View File

@@ -0,0 +1,3 @@
import os
token = os.environ['TELEGRAM_TOKEN']
vk_app_id = os.environ['VK_APP']

View File

@@ -1,77 +0,0 @@
# Создание и развёртывание бота.
В первую очередь, _почему же нужно создавать именно своего бота?_
- Ограничения <a href="https://core.telegram.org/bots/faq#broadcasting-to-users" target="_blank">Telegram</a>
- Ограничения <a href="https://vk.com/dev/api_requests?f=3.1.%20%D0%A7%D0%B0%D1%81%D1%82%D0%BE%D1%82%
D0%BD%D1%8B%D0%B5%20%D0%BE%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D0%BD%D0%B8%D1%8F" target="_blank">VK</a>
- Гарантия сохранности переписки и полного контроля над ботом
Бот имеет полностью открытый исходный код, с которым можно ознакомиться перейдя по ссылке _View on GitHub_ в верхнем углу станицы.
# ⚠️ ВНИМАНИЕ
Гайд далее рассказывает об устаревшем методе установки бота на heroku. К сожалению, пришлось забросить такой вариант из-за модификаций бота и отключения старых VK API. Исходники старого бота лежат в ветке [heroku-old](https://github.com/Kylmakalle/tgvkbot/tree/heroku-old)
Для энтузиастов есть [инструкция](https://github.com/Kylmakalle/tgvkbot/blob/master/README.md) по установке бота на свой сервер.
Если вы боитесь командной строки, рекомендую воспользоваться готовым ботом от разработчика [@tgVKbot](https://t.me/tgVKbot)
## Heroku
Развёртывать бота мы будем на Heroku, поэтому там нужно завести аккаунт <a href="https://heroku.com" target="_blank">heroku.com</a>
Т.к мы будем использовать бесплатный тип хостинга, то на него накладываются некоторые <a href="https://devcenter.heroku.com/articles/free-dyno-hours#usage" target="_blank">ограничения</a>, дабы частично их смягчить можно <a href="https://dashboard.heroku.com/account/billing" target="_blank">привязать карту</a> (никаких плат не взимается), тогда бот сможет работать полный месяц без остановок (а не предусмотренные 550ч/мес изначально)
Подробнее можно ознакомиться <a href="https://devcenter.heroku.com/articles/free-dyno-hours" target="_blank">тут</a>
## VK
По <a href="https://vk.com/editapp?act=create" target="_blank">ссылке</a> нужно создать своё Standalone-приложение, затем во вкладке _Настройки_ перевести _Состояние_ в риложение включено и видно всем_, не забудьте сохранить изменения!
На этом этапе мы сохраняем себе куда-нибудь **ID приложения**
## Telegram
<a href="https://t.me/BotFather" target="_blank">Создаём бота</a>, в качестве имени удобно будет использовать _VK_, юзернейм роли не играет. Сохраняем **токен бота**, который мы получили от BotFather
_Опционально:_
Настройка команд бота
```
dialogs - Список диалогов
search - Поиск диалогов
chat - Текущий чат
leave - Покинуть текущий чат
start - Подключить ВК
stop - Отключить ВК
```
[Логотип VK](vklogo.jpg) для аватарки бота.
<h2><a name="heroku--vk--telegram"></a>Heroku + VK + Telegram</h2>
Получив ID приложения и токен бота, можно смело начинать развёртывать бота на Heroku.
Нажав на кнопку ниже вы попадёте на страницу развёртывания, где будут поля:
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/Kylmakalle/tgvkbot/tree/master)
- **Имя приложения** - можно оставить пустым, Heroku выберет за вас.
- **Регион развёртывания** - выбираем Европу, чтобы бот был пошустрее.
- **Telegram API Token** - токен бота полученный ранее.
- **VK APP ID** - ID приложения ВКонтакте полученный ранее.
После успешного развёртывания можно смело пользоваться ботом.
Если вы вдруг что-то напутали с токеном и/или ID, их всегда можно настроить перейдя в _Settings->Config Vars_ соответствующего приложения в своей панели управления <a href="https://dashboard.heroku.com/ " target="_blank">dashboard.heroku.com</a>
Если всё прошло гладко, то можно ознакомиться с [Использованием](/tgvkbot/usage/)
## Обновление
Обо всех **ВАЖНЫХ** обновлениях можно будет узнать в <a href="https://t.me/tg_vk" target="_blank">Telegram канале</a>
К сожалению простого механизма обновления бота нет и не предусмотрено, поэтому единственный способ - повторное <a href="#heroku--vk--telegram">развёртывание</a> бота на Heroku с уже имеющимися токеном бота и ID приложения из VK.
При обновлении **обязательно** удалить предыдущее приложение с ботом на Heroku. Делается это в _Settings_ соответствующего приложения, в самом низу страницы. После обновления не забудьте заново залогиниться ВК с помощью `/start`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

6
requirements.txt Normal file
View File

@@ -0,0 +1,6 @@
pyTelegramBotAPI
vk
redis
Pillow
ujson
wget

1
runtime.txt Normal file
View File

@@ -0,0 +1 @@
python-3.6.1

View File

@@ -1,43 +0,0 @@
# Использование
Команды:
`/start` - Логин ВКонтакте.
`/dialogs` - Вывод меню со списком диалогов.
`/search Иван` - Поиск диалогов по запросу (в данном случае `Иван`), при вызове команды без запроса бот автоматически спросит что искать, следующим шагом можно будет ввести запрос.
Поддерживаются части слов, транслит, а так же абракадабра, которую вы ненароком напишете забыв сменить раскладку.
`/chat` - Бот сообщает о том. в каком чате вы сейчас находитесь, все дальнеёшие сообщения без _Reply_ будут отправляться именно в этот чат.
`/leave` - Вы покидаете текущий чат, дальнейшее общение происходит только с помощью _Reply_ (или при выборе чата из `/search`, `/dialogs`)
`/stop` - Выход из ВКонтакте, бот полностью стирает всю информацию, для дальнейшей работы требуется повторная инициализация `/start`
Бот отправляет личные сообщения в таком виде:
<p align="center"><img src ="/tgvkbot/assets/rsz_privatemessage.jpg" /></p>
Для сообщений из групповых чатов будет указываться чат после имени отправителя
<p align="center"><img src ="/tgvkbot/assets/rsz_groupmessage.jpg" /></p>
Ответ в чат откуда пришло сообщение (ЛС/Беседа) производится с помощью функции _Reply_ в самом _Telegram_
<p align="center"><img src ="/tgvkbot/assets/rsz_reply.jpg" /></p>
Однако, не нужно делать _Reply_ если вы находитесь в определённом чате (проверить можно командой `/chat`)
**Пример:** Если вы находитесь в чате А и вам пришло сообщение из чата Б, то сделав _Reply_ на сообщение из чата Б, вы отправите сообщение в чат Б и **сообщение отправится только в чат Б, без дублей в чат А!** При этом вы останетесь в чате А и сможете дальше продолжить общаться в чате А без использования _Reply_.
Тем самым **функция Reply имеет приоритет!**
Для "пересылки" сообшения написанного ВК в сам чат ВК, нужно написать `!` первым символом в сообщении (подписи к фото/документу) отправленного из Telegram.
<p align="center"><img src ="/tgvkbot/assets/rsz_forwardtg.jpg" /></p>
<p align="center"><img src ="/tgvkbot/assets/forwardvk.PNG" /></p>
По возможности бот будет отправлять вложения из ВК как документы, или давать ссылку с превью. Если же это невозможно, бот даст прямую ссылку на файл. <del>Исключение на получение <a href="https://vc.ru/n/vk-music-noapi" target="_blank">аудио</a>, для них будет даваться ссылка на раздел аудио VK.</del> Отправка музыкальных файлов из Telegram **поддерживается**, однако вся музыка конвертируется в расширение `.aac` GIF из Telegram отправляются как видео VK.
- [Канал в Telegram](https://t.me/tg_vk) - обновления, поддержа

367
vk_messages.py Normal file
View File

@@ -0,0 +1,367 @@
import logging
import os
import redis
import requests
import time
import ujson
import vk
import wget
VK_POLLING_VERSION = '3.0'
logging.basicConfig(format='%(levelname)-8s [%(asctime)s] %(message)s', level=logging.WARNING, filename='vk.log')
vk_tokens = redis.from_url(os.environ.get("REDIS_URL"))
class VkPolling:
def __init__(self):
self._running = True
def terminate(self):
self._running = False
def run(self, vk_user, bot, chat_id):
while self._running:
timeout = 30
try:
updates = vk_user.get_new_messages()
if updates:
handle_updates(vk_user, bot, chat_id, updates)
except requests.exceptions.ReadTimeout:
logging.warning('Retrying VK Polling.')
timeout = 0
for i in range(timeout):
if self._running:
time.sleep(0.1)
else:
break
def handle_messages(m, vk_user, bot, chat_id, mainmessage=None):
api = vk.API(vk_user.session, v=VK_POLLING_VERSION)
if m['uid'] > 0:
user = api.users.get(user_ids=m["uid"], fields=[])[0]
else:
group = api.groups.getById(group_ids=str(m['uid'])[1:])[0]
user = {'first_name': group['name'], 'last_name': None}
if 'body' in m and not 'attachment' in m and not 'geo' in m and not 'fwd_messages' in m:
data = add_user_info(m, user["first_name"], user["last_name"])[:-1] + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m), reply_to_message_id=mainmessage).wait()
if 'attachment' in m:
attachment_handler(m, user, bot, chat_id, mainmessage)
if 'geo' in m:
data = add_user_info(m, user["first_name"], user["last_name"]) + '<i>Местоположение</i>' + add_reply_info(m)
geo = bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m), reply_to_message_id=mainmessage).wait()
if 'place' in m['geo'] and 'city' in m['geo']['place'] and 'title' in m[geo]['place']:
bot.send_venue(chat_id, m['geo']['coordinates'].split(' ')[0], m['geo']['coordinates'].split(' ')[1],
m['geo']['place']['title'], m['geo']['place']['city'],
disable_notification=check_notification(m),
reply_to_message_id=geo.message_id).wait()
else:
bot.send_location(chat_id, m['geo']['coordinates'].split(' ')[0], m['geo']['coordinates'].split(' ')[1],
disable_notification=check_notification(m),
reply_to_message_id=geo.message_id).wait()
if 'fwd_messages' in m:
data = add_user_info(m, user["first_name"],
user["last_name"]) + '<i>Пересланные сообщения</i>' + add_reply_info(m)
reply = bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m),
reply_to_message_id=mainmessage).wait().message_id
for forwared in m['fwd_messages']:
handle_messages(forwared, vk_user, bot, chat_id, reply)
def handle_updates(vk_user, bot, chat_id, updates):
for m in updates:
if not m['out']:
handle_messages(m, vk_user, bot, chat_id)
def attachment_handler(m, user, bot, chat_id, mainmessage=None):
for attach in m['attachments']:
if attach['type'] == 'photo':
try:
data = add_user_info(m, user['first_name'], user['last_name']) + '<a href="{}">Фото</a>'.format(
get_max_src(attach['photo'])) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m), reply_to_message_id=mainmessage).wait()
except:
send_doc_link(attach, m, user, bot, chat_id, mainmessage)
elif attach['type'] == 'video':
try:
link = 'https://vk.com/video{}_{}'.format(attach['video']['owner_id'],
attach['video']['vid'])
data = add_user_info(m, user['first_name'], user['last_name']) + '<a href="{}">Видео</a>'.format(
link) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m), reply_to_message_id=mainmessage).wait()
except:
send_doc_link(attach, m, user, bot, chat_id, mainmessage)
elif attach['type'] == 'audio':
headers = {'content-type': 'application/json'}
audio = requests.get('https://asergey.me/vkmusapi/',
json={'aid': attach['audio']['aid'], 'owner_id': attach['audio']['owner_id']},
headers=headers)
try:
if audio.status_code == 200 and audio.json()['ok']:
audio_dict = audio.json()
data = add_user_info(m, user["first_name"],
user["last_name"]) + '🎧 <i>Аудиозапись</i>' + add_reply_info(m)
audio_msg = bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m),
reply_to_message_id=mainmessage).wait()
action = bot.send_chat_action(chat_id, 'upload_document')
bot.send_audio(chat_id, audio_dict['vk_response']['url'],
duration=audio_dict['vk_response']['duration'],
title=audio_dict['vk_response']['title'],
performer=audio_dict['vk_response']['artist'],
disable_notification=check_notification(m),
reply_to_message_id=audio_msg.message_id).wait()
action.wait()
else:
data = add_user_info(m, user['first_name'], user[
'last_name']) + '🎧 <a href="https://m.vk.com/audio?q={}%20-%20{}">{} - {}</a>'.format(
attach['audio']['artist'].replace(' ', '%20'),
attach['audio']['title'].replace(' ', '%20'), attach['audio']['artist'],
attach['audio']['title']) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m), reply_to_message_id=mainmessage).wait()
except Exception as e:
print(e)
data = add_user_info(m, user['first_name'], user[
'last_name']) + '🎧 <a href="https://m.vk.com/audio?q={}%20-%20{}">{} - {}</a>'.format(
attach['audio']['artist'].replace(' ', '%20'),
attach['audio']['title'].replace(' ', '%20'), attach['audio']['artist'],
attach['audio']['title']) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m), reply_to_message_id=mainmessage).wait()
elif attach['type'] == 'doc':
if attach['doc']['ext'] == 'gif':
try:
link = attach['doc']['url']
data = add_user_info(m, user["first_name"], user["last_name"]) + '<a href="{}">GIF</a>'.format(
link) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m),
reply_to_message_id=mainmessage).wait()
except:
send_doc_link(attach, m, user, bot, chat_id, mainmessage)
elif attach['doc']['ext'] == 'pdf' or attach['doc']['ext'] == 'zip':
try:
link = attach['doc']['url']
data = add_user_info(m, user["first_name"],
user["last_name"]) + '<a href="{}">Документ</a>'.format(
link) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m),
reply_to_message_id=mainmessage).wait()
except:
send_doc_link(attach, m, user, bot, chat_id, mainmessage)
elif attach['doc']['ext'] == 'jpg' or attach['doc']['ext'] == 'png':
try:
link = attach['doc']['url']
data = add_user_info(m, user["first_name"],
user["last_name"], ) + '<i>Документ</i>' + add_reply_info(m)
notification = bot.send_message(chat_id, data, parse_mode='HTML',
disable_notification=check_notification(m),
reply_to_message_id=mainmessage).wait()
uploading = bot.send_chat_action(chat_id, 'upload_document')
bot.send_document(chat_id, link, reply_to_message_id=notification.message_id,
disable_notification=check_notification(m)).wait()
uploading.wait()
except:
send_doc_link(attach, m, user, bot, chat_id, mainmessage)
elif attach['doc']['ext'] == 'ogg':
try:
link = attach['doc']['url']
data = add_user_info(m, user["first_name"], user["last_name"], ) + \
'<a href="{}">Аудио</a>'.format(link) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m),
reply_to_message_id=mainmessage).wait()
except:
send_doc_link(attach, m, user, bot, chat_id, mainmessage)
elif attach['doc']['ext'] == 'doc' or attach['doc']['ext'] == 'docx':
try:
data = add_user_info(m, user["first_name"],
user["last_name"], ) + '<i>Документ</i>' + add_reply_info(m)
notification = bot.send_message(chat_id, data, parse_mode='HTML',
disable_notification=check_notification(m),
reply_to_message_id=mainmessage).wait()
uploading = bot.send_chat_action(chat_id, 'upload_document')
file = wget.download(requests.get(attach['doc']['url']).url)
openedfile = open(file, 'rb')
bot.send_document(chat_id, openedfile,
reply_to_message_id=notification.message_id,
disable_notification=check_notification(m)).wait()
uploading.wait()
openedfile.close()
os.remove(file)
except:
send_doc_link(attach, m, user, bot, chat_id, mainmessage)
else:
send_doc_link(attach, m, user, bot, chat_id, mainmessage)
elif attach['type'] == 'sticker':
link = attach['sticker']['photo_512']
data = add_user_info(m, user["first_name"], user["last_name"]) + '<a href="{}">Стикер</a>'.format(
link) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m), reply_to_message_id=mainmessage).wait()
elif attach['type'] == 'wall':
link = 'https://vk.com/wall{}_{}'.format(attach['wall']['from_id'], attach['wall']['id'])
data = add_user_info(m, user["first_name"], user["last_name"]) + '<a href="{}">Запись на стене</a>'.format(
link) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m), reply_to_message_id=mainmessage).wait()
elif attach['type'] == 'wall_reply':
data = add_user_info(m, user["first_name"],
user["last_name"]) + '<i>Комментарий на стене</i>' + add_reply_info(m)
comment = bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m),
reply_to_message_id=mainmessage).wait()
try:
api = vk.API(get_session(vk_tokens.get(str(chat_id))), v=VK_POLLING_VERSION)
user = api.users.get(user_ids=attach['wall_reply']["uid"], fields=[])[0]
if attach['wall_reply']['text']:
data = add_user_info(m, user["first_name"], user["last_name"]) + \
attach['wall_reply']['text'].replace('<br>', '\n') + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m),
reply_to_message_id=comment.message_id).wait()
if 'attachments' in attach['wall_reply']:
attachment_handler(attach['wall_reply'], user, bot, chat_id, mainmessage=comment.message_id)
except:
link = 'https://vk.com/wall{}_{}'.format(attach['wall']['owner_id'], attach['wall']['cid'])
data = add_user_info(m, user["first_name"],
user["last_name"]) + '<a href="{}">Комментарий</a>'.format(
link) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m),
reply_to_message_id=comment.message_id).wait()
def check_expansion(document):
if len(document['doc']['title'].split('.')) - 1:
return document['doc']['title']
else:
return document['doc']['title'] + '.' + document['doc']['ext']
def send_doc_link(doc, m, user, bot, chat_id, mainmessage=None):
link = doc['doc']['url']
data = add_user_info(m, user["first_name"], user["last_name"]) + \
'<i>Документ</i>\n<a href="{}">{}</a>'.format(link, check_expansion(doc)) + add_reply_info(m)
bot.send_message(chat_id, data, parse_mode='HTML', disable_web_page_preview=False,
disable_notification=check_notification(m), reply_to_message_id=mainmessage).wait()
def check_forward_id(msg):
if 'mid' in msg:
return msg['mid']
else:
return None
def add_reply_info(m):
if 'chat_id' in m:
return '<a href="x{}.{}.{}">&#8203;</a>'.format(m['uid'], m['chat_id'], check_forward_id(m))
else:
return '<a href="x{}.{}.00">&#8203;</a>'.format(m['uid'], check_forward_id(m))
def add_user_info(m, first_name, last_name):
if 'body' in m and m['body']:
if last_name:
if 'chat_id' in m:
return '<b>{} {} @ {}:</b>\n{}\n'.format(first_name, last_name, m['title'],
m['body'].replace('<br>', '\n'))
else:
return '<b>{} {}:</b>\n{}\n'.format(first_name, last_name, m['body'].replace('<br>', '\n'))
else:
if 'chat_id' in m:
return '<b>{} @ {}:</b>\n{}\n'.format(first_name, m['title'],
m['body'].replace('<br>', '\n'))
else:
return '<b>{}:</b>\n{}\n'.format(first_name, m['body'].replace('<br>', '\n'))
else:
if last_name:
if 'chat_id' in m:
return '<b>{} {} @ {}:</b>\n'.format(first_name, last_name, m['title'])
else:
return '<b>{} {}:</b>\n'.format(first_name, last_name)
else:
if 'chat_id' in m:
return '<b>{} @ {}:</b>\n'.format(first_name, m['title'])
else:
return '<b>{}:</b>\n'.format(first_name)
def check_notification(value):
if 'push_settings' in value:
return True
else:
return False
def get_max_src(attachment):
if 'src_xxbig' in attachment:
return attachment['src_xxbig']
if 'src_xbig' in attachment:
return attachment['src_xbig']
if 'src_big' in attachment:
return attachment['src_big']
if 'src' in attachment:
return attachment['src']
class VkMessage:
def __init__(self, token):
self.session = get_session(token)
self.ts, self.pts = get_tses(self.session)
def get_new_messages(self):
api = vk.API(self.session, v=VK_POLLING_VERSION)
try:
ts_pts = ujson.dumps({"ts": self.ts, "pts": self.pts})
new = api.execute(code='return API.messages.getLongPollHistory({});'.format(ts_pts))
except vk.api.VkAPIError:
timeout = 3
logging.warning('Retrying getLongPollHistory in {} seconds'.format(timeout))
time.sleep(timeout)
self.ts, self.pts = get_tses(self.session)
ts_pts = ujson.dumps({"ts": self.ts, "pts": self.pts})
new = api.execute(code='return API.messages.getLongPollHistory({});'.format(ts_pts))
msgs = new['messages']
self.pts = new["new_pts"]
count = msgs[0]
res = []
if count == 0:
pass
else:
res = msgs[1:]
return res
def get_session(token):
return vk.Session(access_token=token)
def get_tses(session):
api = vk.API(session, v=VK_POLLING_VERSION)
ts = api.messages.getLongPollServer(need_pts=1)
return ts['ts'], ts['pts']