Compare commits
119 Commits
heroku-old
...
gh-pages
Author | SHA1 | Date | |
---|---|---|---|
|
4b6f01c18f | ||
|
96b306c667 | ||
|
dafe9a1d4e | ||
|
9b254aa51b | ||
|
16c51ca6bf | ||
|
07e287ce09 | ||
|
5663b2eb68 | ||
|
d0ef969991 | ||
|
975ac7b498 | ||
|
c43a49a896 | ||
|
5a7787714f | ||
|
7adc464167 | ||
|
4c4f906362 | ||
|
9967a0e243 | ||
|
957ddbb5ee | ||
|
08c9de3a18 | ||
|
b10c7ed50b | ||
|
ef27a2bbc2 | ||
|
cf2ef9699d | ||
|
11425341a4 | ||
|
4d1de3c0e8 | ||
|
19fdd09807 | ||
|
42e6824374 | ||
|
7c390ad20c | ||
|
1ebc24514a | ||
|
4a70210611 | ||
|
a90103be60 | ||
|
90959141aa | ||
|
f8f3345e5e | ||
|
f2cd1f2f2b | ||
|
63884f27f5 | ||
|
9c9db0f6c9 | ||
|
af1b80e530 | ||
|
e571d89f6b | ||
|
98fb8eff9a | ||
|
b69db84f38 | ||
|
cce30f8607 | ||
|
c6687a438d | ||
|
a6366b0364 | ||
|
bd02529577 | ||
|
ee27cd71f0 | ||
|
351bc0c0b5 | ||
|
6aa79999de | ||
|
ea60c6e8eb | ||
|
0875f5d3ec | ||
|
0e4c2bcd04 | ||
|
94c368d37c | ||
|
fdba66bf74 | ||
|
332e85b746 | ||
|
3d7cea9ea3 | ||
|
9877b37bd8 | ||
|
4854be2d54 | ||
|
3f4f699414 | ||
|
a755851005 | ||
|
bed0fa48c6 | ||
|
71940447c3 | ||
|
32e245d603 | ||
|
c19ac8072e | ||
|
4c17b25e25 | ||
|
b4faa4871b | ||
|
e456763ae3 | ||
|
6f1bba53bd | ||
|
c39613b5fa | ||
|
29f624ab63 | ||
|
f9055a98a6 | ||
|
bf5b7e1e65 | ||
|
c8e383ba23 | ||
|
e821889fc8 | ||
|
775fed0dc6 | ||
|
bb72f21273 | ||
|
01634f8d91 | ||
|
8501360e31 | ||
|
496dd9870b | ||
|
7578db792c | ||
|
a05bb58737 | ||
|
4bf5562a77 | ||
|
4a76321df1 | ||
|
23f417e984 | ||
|
bfadd61553 | ||
|
a79d2bb212 | ||
|
1e0ac1bf42 | ||
|
526640eea7 | ||
|
e70837a6e7 | ||
|
132cd2eb8d | ||
|
d514fb3ad0 | ||
|
1a3add8f92 | ||
|
4279f2549e | ||
|
691b18e625 | ||
|
8eaadad5a7 | ||
|
172979b7c7 | ||
|
869a748956 | ||
|
871f69c5d7 | ||
|
bdca43555c | ||
|
2fdfa810d4 | ||
|
c72d8b7adf | ||
|
fae379f9b4 | ||
|
6364f41ce8 | ||
|
98b4d0e0d7 | ||
|
5057b8a7c4 | ||
|
c353355da5 | ||
|
4e405a79ed | ||
|
d263380b80 | ||
|
28a10393b0 | ||
|
514a2a5325 | ||
|
d0f21dfd10 | ||
|
3101d6636d | ||
|
aff9a7d2eb | ||
|
335b51397d | ||
|
2da398486a | ||
|
f92a2cb6e5 | ||
|
b08fffea7e | ||
|
6ef93f949c | ||
|
308cb0a54b | ||
|
51b3fe5349 | ||
|
08a0e1662e | ||
|
05b8410776 | ||
|
a2685c7ac7 | ||
|
537d7648fe | ||
|
1f2cc35306 |
17
.gitattributes
vendored
@@ -1,17 +0,0 @@
|
|||||||
# 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
@@ -1,145 +0,0 @@
|
|||||||
# 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
@@ -1,20 +1,16 @@
|
|||||||
MIT License
|
Copyright (c) 2017 А. Сергей
|
||||||
|
|
||||||
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.
|
|
||||||
|
35
README.md
@@ -1,28 +1,27 @@
|
|||||||
### Новая версия в разработке!
|
# Общие функции
|
||||||
Актуальная ветка разработки находится [здесь](https://github.com/Kylmakalle/tgvkbot/tree/async-dev)
|
|
||||||
|
|
||||||
Подробнее в [чате](https://t.me/joinchat/BZq6jwxeTh04qBzilM5x3g)
|
- [Создание и развёртывание](installation/README.md)
|
||||||
|
- [Использование](usage/README.md)
|
||||||
|
- [Канал в Telegram](https://t.me/tg_vk) - обновления, поддержа
|
||||||
|
|
||||||
# tgvkbot
|
Бот позволяет обмениваться сообщениями ВК используя Telegram, имеется возможность пересылки сообщений, а так же выбора диалога.
|
||||||
Бот позволяет получать и отправлять сообщения VK находясь в Telegram
|
<p align="center"><img src ="assets/rsz_sunset (1).jpg" /></p>
|
||||||
|
|
||||||
https://www.asergey.me/tgvkbot
|
Поддерживаются различные вложения
|
||||||
|
|
||||||
[](https://heroku.com/deploy)
|
<p align="center"><img src ="assets/documents.PNG" /></p>
|
||||||
|
|
||||||
|
|
||||||
[Ветка](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)
|
И поиск по диалогам
|
||||||
|
|
||||||
Бэкенд API для получения музыки через https://asergey.me/vkmusapi/
|
<p align="center"><img src ="assets/rsz_search (1).jpg" /></p>
|
||||||
|
|
||||||
https://gist.github.com/Kylmakalle/e63902025c527ac3610989530f4fa417
|
- [Создание и развёртывание](installation/README.md)
|
||||||
|
- [Использование](usage/README.md)
|
||||||
|
- [Канал в Telegram](https://t.me/tg_vk) - обновления, поддержа
|
||||||
|
|
||||||
## Stay Tuned!
|
|
||||||
|
|
||||||
|
|
||||||
_Полноценные комментарии к коду будут чуть позже_
|
|
||||||
|
4
_config.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
theme: jekyll-theme-slate
|
||||||
|
title: tgvkbot
|
||||||
|
description: 'Общайтесь ВКонтакте с помощью Telegram бота'
|
||||||
|
google_analytics: UA-101930895-1
|
10
_includes/google-analytics.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<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>
|
425
_sass/jekyll-theme-slate.scss
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
209
_sass/rouge-github.scss
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
.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
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
5
assets/css/style.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
@import "jekyll-theme-slate";
|
||||||
|
@import "style.css";
|
BIN
assets/dialogs.jpg
Normal file
After Width: | Height: | Size: 111 KiB |
BIN
assets/documents.PNG
Normal file
After Width: | Height: | Size: 142 KiB |
BIN
assets/forwardvk.PNG
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/groupmessage.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
assets/privatemessage.jpg
Normal file
After Width: | Height: | Size: 136 KiB |
BIN
assets/rsz_dialogs (1).jpg
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
assets/rsz_forwardtg.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
assets/rsz_groupmessage.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/rsz_privatemessage.jpg
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
assets/rsz_reply.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
assets/rsz_search (1).jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
assets/rsz_sunset (1).jpg
Normal file
After Width: | Height: | Size: 41 KiB |
1
assets/sample.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
BIN
assets/sticker.jpg
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
assets/sunset.jpg
Normal file
After Width: | Height: | Size: 87 KiB |
BIN
assets/vk_music.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
721
bot.py
@@ -1,721 +0,0 @@
|
|||||||
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('<', '<')
|
|
||||||
text = text.replace('>', '>')
|
|
||||||
text = text.replace('&', '&')
|
|
||||||
text = text.replace('©', '©')
|
|
||||||
text = text.replace('®', '®')
|
|
||||||
text = text.replace('«', '«')
|
|
||||||
text = text.replace('»', '«')
|
|
||||||
text = text.replace('°', '°')
|
|
||||||
text = text.replace('™', '™')
|
|
||||||
text = text.replace('±', '±')
|
|
||||||
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(), '/', {'/': {}})"""
|
|
@@ -1,3 +0,0 @@
|
|||||||
import os
|
|
||||||
token = os.environ['TELEGRAM_TOKEN']
|
|
||||||
vk_app_id = os.environ['VK_APP']
|
|
77
installation/README.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# Создание и развёртывание бота.
|
||||||
|
|
||||||
|
В первую очередь, _почему же нужно создавать именно своего бота?_
|
||||||
|
- Ограничения <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.
|
||||||
|
|
||||||
|
Нажав на кнопку ниже вы попадёте на страницу развёртывания, где будут поля:
|
||||||
|
|
||||||
|
[](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`
|
BIN
installation/vklogo.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
@@ -1,6 +0,0 @@
|
|||||||
pyTelegramBotAPI
|
|
||||||
vk
|
|
||||||
redis
|
|
||||||
Pillow
|
|
||||||
ujson
|
|
||||||
wget
|
|
@@ -1 +0,0 @@
|
|||||||
python-3.6.1
|
|
43
usage/README.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Использование
|
||||||
|
|
||||||
|
Команды:
|
||||||
|
|
||||||
|
`/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
@@ -1,367 +0,0 @@
|
|||||||
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{}.{}.{}">​</a>'.format(m['uid'], m['chat_id'], check_forward_id(m))
|
|
||||||
else:
|
|
||||||
return '<a href="x{}.{}.00">​</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']
|
|