add chat
This commit is contained in:
parent
168c5b7f41
commit
3116f4bcd0
|
@ -0,0 +1,5 @@
|
|||
from channels.routing import ProtocolTypeRouter
|
||||
|
||||
application = ProtocolTypeRouter({
|
||||
# (http->django views is added by default)
|
||||
})
|
|
@ -31,6 +31,7 @@ ALLOWED_HOSTS = ['*']
|
|||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'channels',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
|
@ -79,6 +80,7 @@ TEMPLATES = [
|
|||
]
|
||||
|
||||
WSGI_APPLICATION = 'NewMediaMonitoring.wsgi.application'
|
||||
ASGI_APPLICATION = 'NewMediaMonitoring.routing.application'
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
|
||||
|
@ -149,6 +151,12 @@ CORS_ORIGIN_ALLOW_ALL = True
|
|||
|
||||
CORS_URLS_REGEX = r'^/polls/.*$'
|
||||
|
||||
SMS_REGION = 'cn-hangzhou'
|
||||
|
||||
SMS_ACCESS_KEY_ID = 'LTAIBfgqfC2fpIDF'
|
||||
|
||||
SMS_ACCESS_KEY_SECRET = 'ocBzC2UvguYbyR6coNGYdPiV5HdWbC'
|
||||
|
||||
try:
|
||||
from .local_settings import *
|
||||
except ImportError as e:
|
||||
|
|
|
@ -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"],
|
||||
},
|
||||
)
|
|
@ -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
|
|
@ -1,18 +1,23 @@
|
|||
from django.db import models
|
||||
import uuid
|
||||
import datetime
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from dashboard.models import Group
|
||||
|
||||
VERIFY_CODE_TYPE_CHOICES = (
|
||||
(0, 'register'),
|
||||
(1, 'password_recover'),
|
||||
)
|
||||
|
||||
|
||||
class VerifyCode(models.Model):
|
||||
id = models.UUIDField('id', primary_key=True, default=uuid.uuid4)
|
||||
code = models.CharField('code', max_length=8, null=False)
|
||||
phone = models.CharField('phone', max_length=11, 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)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
|
||||
|
@ -23,6 +28,73 @@ class VerifyCode(models.Model):
|
|||
now = datetime.datetime.now()
|
||||
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):
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
from aliyunsdkcore.client import AcsClient
|
||||
from aliyunsdkcore.request import CommonRequest
|
||||
|
||||
import requests
|
||||
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):
|
||||
client = AcsClient('LTAIBfgqfC2fpIDF', 'ocBzC2UvguYbyR6coNGYdPiV5HdWbC', 'cn-hangzhou')
|
||||
client = AcsClient(settings.SMS_ACCESS_KEY_ID,
|
||||
settings.SMS_ACCESS_KEY_SECRET, settings.SMS_REGION)
|
||||
request = CommonRequest()
|
||||
request.set_accept_format('json')
|
||||
request.set_domain('dysmsapi.aliyuncs.com')
|
||||
|
@ -22,9 +28,75 @@ def sent_sms_code(phone):
|
|||
response = client.do_action(request)
|
||||
print(type(response.decode('utf8')))
|
||||
|
||||
|
||||
def generate_code():
|
||||
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__':
|
||||
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)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
# Django~=2.2.15
|
||||
# django-bootstrap3
|
||||
# django-simple-captcha
|
||||
# psycopg2-binary
|
||||
# django-cors-headers
|
||||
# aliyun-python-sdk-core-v3
|
||||
|
||||
Django~=2.2.15
|
||||
django-bootstrap3
|
||||
django-simple-captcha
|
||||
psycopg2-binary
|
||||
django-cors-headers
|
||||
aliyun-python-sdk-core-v3
|
||||
channels
|
||||
requests
|
||||
parsel
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue