This commit is contained in:
baoliang 2020-09-03 10:56:28 +08:00
parent 168c5b7f41
commit 3116f4bcd0
11 changed files with 16912 additions and 12 deletions

View File

@ -0,0 +1,5 @@
from channels.routing import ProtocolTypeRouter
application = ProtocolTypeRouter({
# (http->django views is added by default)
})

View File

@ -31,6 +31,7 @@ ALLOWED_HOSTS = ['*']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'channels',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
@ -79,6 +80,7 @@ TEMPLATES = [
] ]
WSGI_APPLICATION = 'NewMediaMonitoring.wsgi.application' WSGI_APPLICATION = 'NewMediaMonitoring.wsgi.application'
ASGI_APPLICATION = 'NewMediaMonitoring.routing.application'
# Database # Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
@ -149,6 +151,12 @@ CORS_ORIGIN_ALLOW_ALL = True
CORS_URLS_REGEX = r'^/polls/.*$' CORS_URLS_REGEX = r'^/polls/.*$'
SMS_REGION = 'cn-hangzhou'
SMS_ACCESS_KEY_ID = 'LTAIBfgqfC2fpIDF'
SMS_ACCESS_KEY_SECRET = 'ocBzC2UvguYbyR6coNGYdPiV5HdWbC'
try: try:
from .local_settings import * from .local_settings import *
except ImportError as e: except ImportError as e:

185
polls/consumers.py Normal file
View File

@ -0,0 +1,185 @@
from django.conf import settings
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from .exceptions import ClientError
from .utils import get_room_or_error
class ChatConsumer(AsyncJsonWebsocketConsumer):
"""
This chat consumer handles websocket connections for chat clients.
It uses AsyncJsonWebsocketConsumer, which means all the handling functions
must be async functions, and any sync work (like ORM access) has to be
behind database_sync_to_async or sync_to_async. For more, read
http://channels.readthedocs.io/en/latest/topics/consumers.html
"""
##### WebSocket event handlers
async def connect(self):
"""
Called when the websocket is handshaking as part of initial connection.
"""
# Are they logged in?
if self.scope["user"].is_anonymous:
# Reject the connection
await self.close()
else:
# Accept the connection
await self.accept()
# Store which rooms the user has joined on this connection
self.rooms = set()
async def receive_json(self, content):
"""
Called when we get a text frame. Channels will JSON-decode the payload
for us and pass it as the first argument.
"""
# Messages will have a "command" key we can switch on
command = content.get("command", None)
try:
if command == "join":
# Make them join the room
await self.join_room(content["room"])
elif command == "leave":
# Leave the room
await self.leave_room(content["room"])
elif command == "send":
await self.send_room(content["room"], content["message"])
except ClientError as e:
# Catch any errors and send it back
await self.send_json({"error": e.code})
async def disconnect(self, code):
"""
Called when the WebSocket closes for any reason.
"""
# Leave all the rooms we are still in
for room_id in list(self.rooms):
try:
await self.leave_room(room_id)
except ClientError:
pass
##### Command helper methods called by receive_json
async def join_room(self, room_id):
"""
Called by receive_json when someone sent a join command.
"""
# The logged-in user is in our scope thanks to the authentication ASGI middleware
room = await get_room_or_error(room_id, self.scope["user"])
# Send a join message if it's turned on
if settings.NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS:
await self.channel_layer.group_send(
room.group_name,
{
"type": "chat.join",
"room_id": room_id,
"username": self.scope["user"].username,
}
)
# Store that we're in the room
self.rooms.add(room_id)
# Add them to the group so they get room messages
await self.channel_layer.group_add(
room.group_name,
self.channel_name,
)
# Instruct their client to finish opening the room
await self.send_json({
"join": str(room.id),
"title": room.title,
})
async def leave_room(self, room_id):
"""
Called by receive_json when someone sent a leave command.
"""
# The logged-in user is in our scope thanks to the authentication ASGI middleware
room = await get_room_or_error(room_id, self.scope["user"])
# Send a leave message if it's turned on
if settings.NOTIFY_USERS_ON_ENTER_OR_LEAVE_ROOMS:
await self.channel_layer.group_send(
room.group_name,
{
"type": "chat.leave",
"room_id": room_id,
"username": self.scope["user"].username,
}
)
# Remove that we're in the room
self.rooms.discard(room_id)
# Remove them from the group so they no longer get room messages
await self.channel_layer.group_discard(
room.group_name,
self.channel_name,
)
# Instruct their client to finish closing the room
await self.send_json({
"leave": str(room.id),
})
async def send_room(self, room_id, message):
"""
Called by receive_json when someone sends a message to a room.
"""
# Check they are in this room
if room_id not in self.rooms:
raise ClientError("ROOM_ACCESS_DENIED")
# Get the room and send to the group about it
room = await get_room_or_error(room_id, self.scope["user"])
await self.channel_layer.group_send(
room.group_name,
{
"type": "chat.message",
"room_id": room_id,
"username": self.scope["user"].username,
"message": message,
}
)
##### Handlers for messages sent over the channel layer
# These helper methods are named by the types we send - so chat.join becomes chat_join
async def chat_join(self, event):
"""
Called when someone has joined our chat.
"""
# Send a message down to the client
await self.send_json(
{
"msg_type": settings.MSG_TYPE_ENTER,
"room": event["room_id"],
"username": event["username"],
},
)
async def chat_leave(self, event):
"""
Called when someone has left our chat.
"""
# Send a message down to the client
await self.send_json(
{
"msg_type": settings.MSG_TYPE_LEAVE,
"room": event["room_id"],
"username": event["username"],
},
)
async def chat_message(self, event):
"""
Called when someone has messaged our chat.
"""
# Send a message down to the client
await self.send_json(
{
"msg_type": settings.MSG_TYPE_MESSAGE,
"room": event["room_id"],
"username": event["username"],
"message": event["message"],
},
)

8
polls/exceptions.py Normal file
View File

@ -0,0 +1,8 @@
class ClientError(Exception):
"""
Custom exception class that is caught by the websocket receive()
handler and translated into a send back to the client.
"""
def __init__(self, code):
super().__init__(code)
self.code = code

View File

@ -1,18 +1,23 @@
from django.db import models from django.db import models
import uuid import uuid
import datetime import datetime
from django.contrib.auth.models import User
from dashboard.models import Group
VERIFY_CODE_TYPE_CHOICES = ( VERIFY_CODE_TYPE_CHOICES = (
(0, 'register'), (0, 'register'),
(1, 'password_recover'), (1, 'password_recover'),
) )
class VerifyCode(models.Model): class VerifyCode(models.Model):
id = models.UUIDField('id', primary_key=True, default=uuid.uuid4) id = models.UUIDField('id', primary_key=True, default=uuid.uuid4)
code = models.CharField('code', max_length=8, null=False) code = models.CharField('code', max_length=8, null=False)
phone = models.CharField('phone', max_length=11, null=False) phone = models.CharField('phone', max_length=11, null=False)
timeouted = models.DateTimeField('timeouted', null=False) timeouted = models.DateTimeField('timeouted', null=False)
category = models.IntegerField('category', choices=VERIFY_CODE_TYPE_CHOICES, default=0) category = models.IntegerField(
'category', choices=VERIFY_CODE_TYPE_CHOICES, default=0)
added = models.DateTimeField(auto_now_add=True) added = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True) updated = models.DateTimeField(auto_now=True)
@ -23,6 +28,73 @@ class VerifyCode(models.Model):
now = datetime.datetime.now() now = datetime.datetime.now()
return now <= self.timeouted return now <= self.timeouted
def __str__(self):
return self.phone + ':' + self.code
class Notice(models.Model):
id = models.UUIDField('id', primary_key=True, default=uuid.uuid4)
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.CharField('内容', max_length=256, null=False)
is_read = models.BooleanField('是否删除', default=False)
added = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ["-added"]
def is_in_progress(self):
now = datetime.datetime.now()
return now <= self.timeouted
def __str__(self): def __str__(self):
return self.phone + ':' + self.code return self.phone + ':' + self.code
MESSAGE_TYPE_CHOICES = (
(0, 'url'),
(1, 'file'),
(2, 'picture')
)
class Message(models.Model):
id = models.UUIDField('id', primary_key=True, default=uuid.uuid4)
category = models.IntegerField(
'category', choices=MESSAGE_TYPE_CHOICES, default=0)
send_from = models.ForeignKey(User, on_delete=models.CASCADE)
send_to = models.ForeignKey(Group, on_delete=models.CASCADE)
added = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
ordering = ["-added"]
def __str__(self):
return self.send_from + ':' + self.send_to
class URLMessage(Message):
title = models.CharField('title', max_length=256, null=False)
description = models.CharField('description', max_length=512, null=False)
image = models.CharField('image', max_length=256, null=True, blank=True)
url = models.CharField('url', max_length=256, null=True, blank=True)
class FileMessage(Message):
title = models.CharField('title', max_length=256, null=False)
file = models.FileField(upload_to='message/file/%Y/%m/%d/', null=True, blank=True)
class ImageMessage(Message):
file = models.FileField(upload_to='resources/image/%Y/%m/%d/', null=True, blank=True)
class MessageRecord(models.Model):
pass
class ChatRecord(models.Model):
pass

View File

@ -1,10 +1,16 @@
from aliyunsdkcore.client import AcsClient from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest from aliyunsdkcore.request import CommonRequest
import requests
import random import random
from parsel import Selector
from channels.db import database_sync_to_async
from .exceptions import ClientError
from django.conf import settings
def sent_sms_code(phone): def sent_sms_code(phone):
client = AcsClient('LTAIBfgqfC2fpIDF', 'ocBzC2UvguYbyR6coNGYdPiV5HdWbC', 'cn-hangzhou') client = AcsClient(settings.SMS_ACCESS_KEY_ID,
settings.SMS_ACCESS_KEY_SECRET, settings.SMS_REGION)
request = CommonRequest() request = CommonRequest()
request.set_accept_format('json') request.set_accept_format('json')
request.set_domain('dysmsapi.aliyuncs.com') request.set_domain('dysmsapi.aliyuncs.com')
@ -22,9 +28,75 @@ def sent_sms_code(phone):
response = client.do_action(request) response = client.do_action(request)
print(type(response.decode('utf8'))) print(type(response.decode('utf8')))
def generate_code(): def generate_code():
return random.randint(1000, 9999) return random.randint(1000, 9999)
def detect_type(url):
if 'mp.weixin.qq.com' in url:
return 'weixin'
elif 'toutiao.com' in url:
return 'toutiao'
elif 'ixigua.com' in url:
return 'xigua'
else:
return 'other'
def parse(url):
t = detect_type(url)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36 QBCore/3.53.1159.400 QQBrowser/9.0.2524.400 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
}
r = requests.get(url, headers=headers)
selector = Selector(text=r.text)
if t == 'weixin':
og_title = selector.xpath(
'//head/meta[@property="og:title"]/@content').get()
og_description = selector.xpath(
'//head/meta[@property="og:description"]/@content').get()
og_url = selector.xpath(
'//head/meta[@property="og:url"]/@content').get()
og_image = selector.xpath(
'//head/meta[@property="og:image"]/@content').get()
elif t == 'xigua':
og_title = selector.xpath(
'//head/meta[@property="og:title"]/@content').get()
og_description = selector.xpath(
'//head/meta[@property="og:description"]/@content').get()
og_url = selector.xpath(
'//head/meta[@property="og:url"]/@content').get()
og_image = selector.xpath(
'//head/meta[@property="og:image"]/@content').get()
elif t == 'toutiao':
og_title = selector.xpath('//head/title/text()')
og_description = selector.xpath(
'//head/meta[@name="description"]/@content').get()
og_url = url
og_image = None
else:
og_title = selector.xpath('//head/title/text()')
og_description = selector.xpath(
'//head/meta[@name="description"]/@content').get()
og_url = url
og_image = None
return (og_title, og_description, og_url, og_image)
@database_sync_to_async
def get_room_or_error(room_id, user):
"""
Tries to fetch a room for the user, checking permissions along the way.
"""
# Check if the user is logged in
if not user.is_authenticated:
raise ClientError("USER_HAS_TO_LOGIN")
if __name__ == '__main__': if __name__ == '__main__':
sent_sms_code('13993199566') # sent_sms_code('13993199566')
og_title, og_description, og_url, og_image = parse(
'https://mp.weixin.qq.com/s/EhX0Pm1e0FAfse0zz9ow8Q')
print(og_title, og_description, og_url, og_image)

View File

@ -1,7 +1,9 @@
# Django~=2.2.15 Django~=2.2.15
# django-bootstrap3 django-bootstrap3
# django-simple-captcha django-simple-captcha
# psycopg2-binary psycopg2-binary
# django-cors-headers django-cors-headers
# aliyun-python-sdk-core-v3 aliyun-python-sdk-core-v3
channels
requests
parsel

4068
static/material/content.txt Normal file

File diff suppressed because one or more lines are too long

4206
static/material/content1.txt Normal file

File diff suppressed because one or more lines are too long

4206
static/material/content2.txt Normal file

File diff suppressed because one or more lines are too long

4068
static/material/content3.txt Normal file

File diff suppressed because one or more lines are too long