add twitter feed
This commit is contained in:
parent
10f3612cf7
commit
dfb584116d
@ -24,5 +24,29 @@
|
|||||||
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||||
</formatting-settings>
|
</formatting-settings>
|
||||||
</DBN-SQL>
|
</DBN-SQL>
|
||||||
|
<DBN-PSQL>
|
||||||
|
<case-options enabled="true">
|
||||||
|
<option name="KEYWORD_CASE" value="lower" />
|
||||||
|
<option name="FUNCTION_CASE" value="lower" />
|
||||||
|
<option name="PARAMETER_CASE" value="lower" />
|
||||||
|
<option name="DATATYPE_CASE" value="lower" />
|
||||||
|
<option name="OBJECT_CASE" value="preserve" />
|
||||||
|
</case-options>
|
||||||
|
<formatting-settings enabled="false" />
|
||||||
|
</DBN-PSQL>
|
||||||
|
<DBN-SQL>
|
||||||
|
<case-options enabled="true">
|
||||||
|
<option name="KEYWORD_CASE" value="lower" />
|
||||||
|
<option name="FUNCTION_CASE" value="lower" />
|
||||||
|
<option name="PARAMETER_CASE" value="lower" />
|
||||||
|
<option name="DATATYPE_CASE" value="lower" />
|
||||||
|
<option name="OBJECT_CASE" value="preserve" />
|
||||||
|
</case-options>
|
||||||
|
<formatting-settings enabled="false">
|
||||||
|
<option name="STATEMENT_SPACING" value="one_line" />
|
||||||
|
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
|
||||||
|
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||||
|
</formatting-settings>
|
||||||
|
</DBN-SQL>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
4
.idea/encodings.xml
Normal file
4
.idea/encodings.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
||||||
|
</project>
|
@ -1,4 +1,4 @@
|
|||||||
{% load staticfiles bootstrap4 cabinet %}
|
{% load staticfiles cabinet %}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html class="fixed dark" lang="en">
|
<html class="fixed dark" lang="en">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{% extends 'cabinet/_internal_base.html' %}
|
{% extends 'cabinet/_internal_base.html' %}
|
||||||
{% load bootstrap4 %}
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<h2 class="card-title">Config</h2>
|
<h2 class="card-title">Config</h2>
|
||||||
</header>
|
</header>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{% bootstrap_form form layout='horizontal' %}
|
{% crispy form %}
|
||||||
</div>
|
</div>
|
||||||
<footer class="card-footer text-right">
|
<footer class="card-footer text-right">
|
||||||
<button type="submit" class="btn btn-primary">Save</button>
|
<button type="submit" class="btn btn-primary">Save</button>
|
||||||
|
@ -19,6 +19,7 @@ ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS', [])
|
|||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'django_extensions',
|
'django_extensions',
|
||||||
'bootstrap4',
|
'bootstrap4',
|
||||||
|
'crispy_forms',
|
||||||
'djconfig',
|
'djconfig',
|
||||||
'django_celery_results',
|
'django_celery_results',
|
||||||
'django_celery_beat',
|
'django_celery_beat',
|
||||||
@ -113,6 +114,8 @@ LOGIN_URL = 'cabinet:login'
|
|||||||
LOGIN_REDIRECT_URL = 'cabinet:index'
|
LOGIN_REDIRECT_URL = 'cabinet:index'
|
||||||
LOGOUT_REDIRECT_URL = LOGIN_URL
|
LOGOUT_REDIRECT_URL = LOGIN_URL
|
||||||
|
|
||||||
|
CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
||||||
|
|
||||||
sentry_sdk.init(
|
sentry_sdk.init(
|
||||||
dsn=env.str('SENTRY_DSN', None),
|
dsn=env.str('SENTRY_DSN', None),
|
||||||
integrations=[DjangoIntegration()],
|
integrations=[DjangoIntegration()],
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from crispy_forms.helper import FormHelper
|
||||||
|
from crispy_forms.layout import Layout, Fieldset
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import ModelForm
|
from django.forms import ModelForm
|
||||||
from djconfig.forms import ConfigForm
|
from djconfig.forms import ConfigForm
|
||||||
@ -28,8 +30,25 @@ class FeedsAppConfigForm(ConfigForm):
|
|||||||
title = 'Feeds'
|
title = 'Feeds'
|
||||||
|
|
||||||
feed_bot_token = forms.CharField(required=True)
|
feed_bot_token = forms.CharField(required=True)
|
||||||
|
|
||||||
russian_proxy_string = forms.CharField()
|
russian_proxy_string = forms.CharField()
|
||||||
vk_username = forms.CharField(required=True)
|
vk_username = forms.CharField(required=True)
|
||||||
vk_password = forms.CharField(required=True)
|
vk_password = forms.CharField(required=True)
|
||||||
vk_auth = forms.CharField(required=False, widget=forms.Textarea())
|
vk_auth = forms.CharField(required=False, widget=forms.Textarea())
|
||||||
anticaptcha_token = forms.CharField()
|
anticaptcha_token = forms.CharField()
|
||||||
|
|
||||||
|
twitter_consumer_api_key = forms.CharField(required=False)
|
||||||
|
twitter_consumer_api_secret = forms.CharField(required=False)
|
||||||
|
twitter_access_token = forms.CharField(required=False)
|
||||||
|
twitter_access_token_secret = forms.CharField(required=False)
|
||||||
|
|
||||||
|
helper = FormHelper()
|
||||||
|
helper.form_class = 'form-horizontal'
|
||||||
|
helper.label_class = 'col-sm-2'
|
||||||
|
helper.field_class = 'col-sm-10'
|
||||||
|
helper.layout = Layout(
|
||||||
|
Fieldset('Global', 'feed_bot_token'),
|
||||||
|
Fieldset('VK.com', 'russian_proxy_string', 'vk_username', 'vk_password', 'vk_auth', 'anticaptcha_token'),
|
||||||
|
Fieldset('Twitter', 'twitter_consumer_api_key', 'twitter_consumer_api_secret', 'twitter_access_token',
|
||||||
|
'twitter_access_token_secret'),
|
||||||
|
)
|
||||||
|
23
feeds/migrations/0005_twitterfeedmoduleconfig.py
Normal file
23
feeds/migrations/0005_twitterfeedmoduleconfig.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 2.1.5 on 2019-03-09 19:11
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('feeds', '0004_vkmusicfeedmoduleconfig'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TwitterFeedModuleConfig',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('screen_name', models.CharField(max_length=256)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -4,7 +4,8 @@ from .vk_music import VKMusicFeedModuleConfig
|
|||||||
from .dank_memes import DankMemesFeedModuleConfig
|
from .dank_memes import DankMemesFeedModuleConfig
|
||||||
from .shitty_watercolour import ShittyWatercolourFeedModuleConfig
|
from .shitty_watercolour import ShittyWatercolourFeedModuleConfig
|
||||||
from .wp_comic import WPComicFeedModuleConfig
|
from .wp_comic import WPComicFeedModuleConfig
|
||||||
|
from .twitter import TwitterFeedModuleConfig
|
||||||
|
|
||||||
FEED_MODULES = [EchoFeedModuleConfig, VKFeedModuleConfig, VKMusicFeedModuleConfig, DankMemesFeedModuleConfig,
|
FEED_MODULES = [EchoFeedModuleConfig, VKFeedModuleConfig, VKMusicFeedModuleConfig, DankMemesFeedModuleConfig,
|
||||||
ShittyWatercolourFeedModuleConfig, WPComicFeedModuleConfig]
|
ShittyWatercolourFeedModuleConfig, WPComicFeedModuleConfig, TwitterFeedModuleConfig]
|
||||||
|
|
||||||
|
50
feeds/modules/twitter.py
Normal file
50
feeds/modules/twitter.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import sentry_sdk
|
||||||
|
import twitter
|
||||||
|
from django.db import models
|
||||||
|
from djconfig import config
|
||||||
|
from telebot import TeleBot
|
||||||
|
from telebot.types import InputMediaPhoto, InputMediaVideo
|
||||||
|
|
||||||
|
from feeds.models import FeedModuleConfig
|
||||||
|
|
||||||
|
|
||||||
|
class TwitterFeedModuleConfig(FeedModuleConfig):
|
||||||
|
screen_name = models.CharField(max_length=256)
|
||||||
|
|
||||||
|
MODULE_NAME = 'Twitter feed'
|
||||||
|
|
||||||
|
def execute(self, bot: TeleBot, chat_id, last_id):
|
||||||
|
config._reload_maybe()
|
||||||
|
|
||||||
|
if last_id is None:
|
||||||
|
last_id = 0
|
||||||
|
|
||||||
|
api = twitter.Api(config.twitter_consumer_api_key, config.twitter_consumer_api_secret,
|
||||||
|
config.twitter_access_token, config.twitter_access_token_secret)
|
||||||
|
for status in reversed(api.GetUserTimeline(screen_name=self.screen_name, since_id=last_id)):
|
||||||
|
try:
|
||||||
|
text = status.text # type: str
|
||||||
|
for url in status.urls:
|
||||||
|
text = text.replace(url.url, url.expanded_url)
|
||||||
|
media = list(filter(lambda m: m.type in ('photo', 'animated_gif', 'video'), status.media))
|
||||||
|
if not len(media):
|
||||||
|
bot.send_message(chat_id, text)
|
||||||
|
elif len(media) == 1:
|
||||||
|
if media[0].type == 'photo':
|
||||||
|
bot.send_photo(chat_id, media[0].media_url_https, text)
|
||||||
|
elif media[0].type == 'animated_gif':
|
||||||
|
bot.send_document(chat_id, media[0].video_info['variants'][-1]['url'], caption=text)
|
||||||
|
else:
|
||||||
|
bot.send_video(chat_id, media[0].video_info['variants'][-1]['url'], caption=text)
|
||||||
|
else:
|
||||||
|
converted_media = []
|
||||||
|
for i, m in enumerate(media):
|
||||||
|
if m.type == 'photo':
|
||||||
|
converted_media.append(InputMediaPhoto(m.media_url_https, text if not i else None))
|
||||||
|
elif m.type in ('animated_gif', 'video'):
|
||||||
|
converted_media.append(InputMediaVideo(m.video_info['variants'][-1]['url'],
|
||||||
|
caption=text if not i else None))
|
||||||
|
bot.send_media_group(chat_id, converted_media)
|
||||||
|
except Exception as e:
|
||||||
|
sentry_sdk.capture_exception(e)
|
||||||
|
yield status.id
|
@ -29,7 +29,7 @@ def execute_feed(feed_pk):
|
|||||||
feed.lock = True
|
feed.lock = True
|
||||||
feed.save()
|
feed.save()
|
||||||
|
|
||||||
bot = TeleBot(config.feed_bot_token)
|
bot = TeleBot(config.feed_bot_token, threaded=False)
|
||||||
for last_id in feed.config.execute(bot, feed.chat_id, feed.last_id):
|
for last_id in feed.config.execute(bot, feed.chat_id, feed.last_id):
|
||||||
if last_id:
|
if last_id:
|
||||||
feed.last_id = last_id
|
feed.last_id = last_id
|
||||||
|
@ -10,6 +10,7 @@ Django==2.1.5
|
|||||||
django-bootstrap4==0.0.7
|
django-bootstrap4==0.0.7
|
||||||
django-celery-beat==1.4.0
|
django-celery-beat==1.4.0
|
||||||
django-celery-results==1.0.4
|
django-celery-results==1.0.4
|
||||||
|
django-crispy-forms==1.7.2
|
||||||
django-djconfig==0.9.0
|
django-djconfig==0.9.0
|
||||||
django-environ==0.4.5
|
django-environ==0.4.5
|
||||||
django-extensions==2.1.4
|
django-extensions==2.1.4
|
||||||
@ -18,20 +19,25 @@ django-redis==4.10.0
|
|||||||
django-timezone-field==3.0
|
django-timezone-field==3.0
|
||||||
django-yamlfield==1.0.3
|
django-yamlfield==1.0.3
|
||||||
enum34==1.1.6
|
enum34==1.1.6
|
||||||
|
future==0.17.1
|
||||||
idna==2.8
|
idna==2.8
|
||||||
kombu==4.2.2.post1
|
kombu==4.2.2.post1
|
||||||
|
oauthlib==3.0.1
|
||||||
Pillow==5.4.1
|
Pillow==5.4.1
|
||||||
psycopg2-binary==2.7.6.1
|
psycopg2-binary==2.7.6.1
|
||||||
pyaes==1.6.1
|
pyaes==1.6.1
|
||||||
-e git+https://github.com/pyrogram/pyrogram@69922e5cf905df7da5a37602be48d063dea4212e#egg=Pyrogram
|
Pyrogram==0.11.0
|
||||||
PySocks==1.6.8
|
PySocks==1.6.8
|
||||||
pyTelegramBotAPI==3.6.6
|
pyTelegramBotAPI==3.6.6
|
||||||
|
python-anticaptcha==0.3.1
|
||||||
python-crontab==2.3.6
|
python-crontab==2.3.6
|
||||||
python-dateutil==2.7.5
|
python-dateutil==2.7.5
|
||||||
|
python-twitter==3.5
|
||||||
pytz==2018.9
|
pytz==2018.9
|
||||||
PyYAML==3.13
|
PyYAML==3.13
|
||||||
redis==3.0.1
|
redis==3.0.1
|
||||||
requests==2.21.0
|
requests==2.21.0
|
||||||
|
requests-oauthlib==1.2.0
|
||||||
sentry-sdk==0.6.9
|
sentry-sdk==0.6.9
|
||||||
six==1.12.0
|
six==1.12.0
|
||||||
soupsieve==1.7.3
|
soupsieve==1.7.3
|
||||||
|
Loading…
Reference in New Issue
Block a user