Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e08a0259a | ||
|
|
a1a21ab269 | ||
|
|
70ae70d20a | ||
|
|
00f407c7d5 | ||
|
|
842a2101bf | ||
|
|
754b5e98c9 | ||
|
|
4f5d38577d | ||
|
|
e6f06b5f38 | ||
|
|
a548fdfbb2 | ||
|
|
421a87a9ec | ||
|
|
fe65f000eb | ||
|
|
7e6001bc1f | ||
|
|
4275b33f24 | ||
|
|
2c1a565dda | ||
|
|
76d644c447 | ||
|
|
55605dd755 | ||
|
|
326348aff4 | ||
|
|
558176dcea | ||
|
|
cf688fe5fc | ||
|
|
c4ec077569 | ||
|
|
80e689446c | ||
|
|
e11b70ad52 | ||
|
|
5e424e792c | ||
|
|
4ac1c79f6f | ||
|
|
a1b36c2c4e | ||
|
|
594bec0cb9 | ||
|
|
3ad4a2a58c | ||
|
|
1bdd3dbc19 | ||
|
|
1b6d488688 | ||
|
|
8e8fee9e2f | ||
|
|
92a2a496d7 | ||
|
|
18528febae | ||
|
|
f0b5cdbfb5 | ||
|
|
d264cc65ba | ||
|
|
73b7c3104d | ||
|
|
d7c4cf1b5f | ||
|
|
20ed342a9e | ||
|
|
1aba12e54d | ||
|
|
1ad9dfc8da | ||
|
|
a2ed0966d0 | ||
|
|
e9d4a2642c | ||
|
|
ecf9b300d1 | ||
|
|
36091e7796 | ||
|
|
147ce255c0 | ||
|
|
66fd29dd02 | ||
|
|
054689e158 | ||
|
|
8ef8418ff4 | ||
|
|
83edc822a2 | ||
|
|
371e0e21b6 | ||
|
|
abea0b82f4 | ||
|
|
3782e53cdd | ||
|
|
d090b04b37 | ||
|
|
3db9a63aca | ||
|
|
f96930dcc8 | ||
|
|
ddad2bb1b2 | ||
|
|
6fa209518e | ||
|
|
306bc8c9ad | ||
|
|
b8857953b3 | ||
|
|
dc9a30e346 | ||
|
|
37e02ffc69 | ||
|
|
e533d6b5fa | ||
|
|
7f8d4f55f1 | ||
|
|
77a79360bc | ||
|
|
b50e370c85 | ||
|
|
7f9f3bbb49 | ||
|
|
585aa4b36e | ||
|
|
745a3df44e |
1
androidh5api.sh
Normal file
@@ -0,0 +1 @@
|
|||||||
|
python3 run.py
|
||||||
0
app/__init__.py
Normal file
BIN
app/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
app/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
app/__pycache__/crud.cpython-310.pyc
Normal file
BIN
app/__pycache__/crud.cpython-311.pyc
Normal file
BIN
app/__pycache__/database.cpython-310.pyc
Normal file
BIN
app/__pycache__/database.cpython-311.pyc
Normal file
BIN
app/__pycache__/main.cpython-310.pyc
Normal file
BIN
app/__pycache__/main.cpython-311.pyc
Normal file
BIN
app/__pycache__/models.cpython-310.pyc
Normal file
BIN
app/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/__pycache__/schemas.cpython-310.pyc
Normal file
BIN
app/__pycache__/schemas.cpython-311.pyc
Normal file
BIN
app/__pycache__/tgMesaage.cpython-310.pyc
Normal file
BIN
app/__pycache__/tgMessage.cpython-310.pyc
Normal file
44
app/crud.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from app import models, schemas
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
from fastapi import HTTPException
|
||||||
|
|
||||||
|
def get_user(db: Session, user_id: int):
|
||||||
|
return db.query(models.User).filter(models.User.id == user_id).first()
|
||||||
|
|
||||||
|
def get_user_by_openid_or_wxid(db: Session, identifier: str):
|
||||||
|
return db.query(models.User).filter((models.User.openid == identifier) | (models.User.wxid == identifier)).first()
|
||||||
|
|
||||||
|
def get_users(db: Session, skip: int = 0, limit: int = 10):
|
||||||
|
return db.query(models.User).offset(skip).limit(limit).all()
|
||||||
|
|
||||||
|
def create_user(db: Session, user: schemas.UserCreate):
|
||||||
|
db_user = models.User(**user.dict())
|
||||||
|
try:
|
||||||
|
db.add(db_user)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_user)
|
||||||
|
except IntegrityError:
|
||||||
|
db.rollback()
|
||||||
|
# wxid的唯一性
|
||||||
|
raise HTTPException(status_code=400, detail="wxid must be unique")
|
||||||
|
return db_user
|
||||||
|
|
||||||
|
def delete_user(db: Session, user: models.User):
|
||||||
|
db.delete(user)
|
||||||
|
db.commit()
|
||||||
|
return user
|
||||||
|
|
||||||
|
# def update_user(db: Session, user: models.User, user_update: schemas.UserUpdate):
|
||||||
|
# for key, value in user_update.dict(exclude_unset=True).items():
|
||||||
|
# setattr(user, key, value)
|
||||||
|
# db.commit()
|
||||||
|
# db.refresh(user)
|
||||||
|
# return user
|
||||||
|
def update_user(db: Session, user: models.User, user_update: schemas.UserUpdate):
|
||||||
|
for var, value in vars(user_update).items():
|
||||||
|
if value is not None:
|
||||||
|
setattr(user, var, value)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(user)
|
||||||
|
return user
|
||||||
16
app/database.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
# SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
|
||||||
|
# engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
|
||||||
|
# SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
# 使用 MySQL 连接
|
||||||
|
# SQLALCHEMY_DATABASE_URL = "mysql+pymysql://hackrobot:Xiao4669805@192.168.31.184:3400/hackrobot"
|
||||||
|
SQLALCHEMY_DATABASE_URL = "mysql+pymysql://hackrobot:Xiao4669805@localhost:3400/hackrobot"
|
||||||
|
|
||||||
|
engine = create_engine(SQLALCHEMY_DATABASE_URL)
|
||||||
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
438
app/main.py
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
from sqlite3 import IntegrityError
|
||||||
|
from fastapi import BackgroundTasks, FastAPI, HTTPException, Depends, Query
|
||||||
|
from fastapi.responses import JSONResponse, PlainTextResponse
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from typing import List, Optional, Any
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from app import models, schemas, crud
|
||||||
|
from app.database import SessionLocal, engine
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from PIL import Image
|
||||||
|
import qrcode
|
||||||
|
import os
|
||||||
|
from fastapi.responses import FileResponse
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
# from app import tgMessage # type: ignore #发送tg消息
|
||||||
|
from datetime import datetime
|
||||||
|
import requests
|
||||||
|
# from tentcentSMS import sendSms
|
||||||
|
import random
|
||||||
|
from tencentcloud.common import credential
|
||||||
|
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
|
||||||
|
from tencentcloud.sms.v20210111 import sms_client, models as smsmodels
|
||||||
|
from tencentcloud.common.profile.client_profile import ClientProfile
|
||||||
|
from tencentcloud.common.profile.http_profile import HttpProfile
|
||||||
|
import hashlib
|
||||||
|
from urllib.parse import urlencode, unquote
|
||||||
|
import time
|
||||||
|
|
||||||
|
# 初始化数据库
|
||||||
|
models.Base.metadata.create_all(bind=engine)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
JSAPI 支付
|
||||||
|
"""
|
||||||
|
key = "zA2hBU6pv6CIBzkW" # 填写通信密钥
|
||||||
|
mchid = "1561724891" # 特写商户号
|
||||||
|
|
||||||
|
# 构造签名函数
|
||||||
|
def sign(attributes):
|
||||||
|
attributes_new = {k: attributes[k] for k in sorted(attributes.keys())}
|
||||||
|
return (
|
||||||
|
hashlib.md5(
|
||||||
|
(unquote(urlencode(attributes_new)) + "&key=" + key).encode(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.hexdigest()
|
||||||
|
.upper()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Item(BaseModel):
|
||||||
|
event: str
|
||||||
|
EventGroupMsg: None = None
|
||||||
|
prirobot_wxidce: None = None
|
||||||
|
type: None = None
|
||||||
|
# event
|
||||||
|
# "":"EventGroupMsg",//事件标示(当前值为群消息事件)
|
||||||
|
# "robot_wxid":"wxid_5hxa04j4z6pg22",//机器人wxid
|
||||||
|
# "robot_name":"",//机器人昵称,一般为空
|
||||||
|
# "type":1,//1/文本消息 3/图片消息 34/语音消息 42/名片消息 43/视频 47/动态表情 48/地理位置 49/分享链接 2000/转账 2001/红包 2002/小程序 2003/群邀请
|
||||||
|
# "from_wxid":"18900134932@chatroom",//群id,群消息事件才有
|
||||||
|
# "from_name":"微群测",//群名字
|
||||||
|
# "final_from_wxid":"sundreamer",//发该消息的用户微信id
|
||||||
|
# "final_from_name":"遗忘悠剑o",//微信昵称
|
||||||
|
# "to_wxid":"wxid_5hxa04j4z6pg22",//接收消息的人id,(一般是机器人收到了,也有可能是机器人发出的消息,别人收到了,那就是别人)
|
||||||
|
# "msg":"图片https://b3logfile.com/bing/20201024.jpg",//消息内容(string/array) 使用时候根据不同的事件标示来定义这个值,字符串类型或者数据类型
|
||||||
|
# "money":0.01 //金额,只有"EventReceivedTransfer"事件才有该参数
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
# 挂载 images 文件夹
|
||||||
|
app.mount("/images", StaticFiles(directory="images"), name="images")
|
||||||
|
|
||||||
|
# 允许所有来源跨域访问(不推荐用于生产环境)
|
||||||
|
origins = ["*"]
|
||||||
|
|
||||||
|
# 设置CORS(跨域资源共享)策略
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=["*"], # 允许所有来源
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"], # 允许所有方法
|
||||||
|
allow_headers=["*"], # 允许所有请求头
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# 依赖注入
|
||||||
|
def get_db():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
# @app.get("/")
|
||||||
|
# def root():
|
||||||
|
# return {"message": "Hello World"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# phoneNum="+8613243889782"
|
||||||
|
def sendSms(phoneNum,verification_code=None):
|
||||||
|
try:
|
||||||
|
cred = credential.Credential("AKIDOaT6SZQ0JL7cqnTryoOhkW1FBqELAizz",
|
||||||
|
"kDPKyMTJs99sKVP1NREkzxKQFo66pIUP")
|
||||||
|
httpProfile = HttpProfile()
|
||||||
|
httpProfile.reqMethod = "POST" # post请求(默认为post请求)
|
||||||
|
httpProfile.reqTimeout = 30 # 请求超时时间,单位为秒(默认60秒)
|
||||||
|
httpProfile.endpoint = "sms.tencentcloudapi.com" # 指定接入地域域名(默认就近接入)
|
||||||
|
clientProfile = ClientProfile()
|
||||||
|
clientProfile.signMethod = "TC3-HMAC-SHA256" # 指定签名算法
|
||||||
|
clientProfile.language = "en-US"
|
||||||
|
clientProfile.httpProfile = httpProfile
|
||||||
|
client = sms_client.SmsClient(cred, "ap-beijing", clientProfile)
|
||||||
|
req = smsmodels.SendSmsRequest()
|
||||||
|
req.SmsSdkAppId = "1400808237"
|
||||||
|
req.SignName = "异度世界公众号"
|
||||||
|
req.TemplateId = "1795415" #验证码为:{1}, 若非本人操作,请忽略。
|
||||||
|
req.TemplateParamSet = [verification_code]
|
||||||
|
newphoneNum = '+86' + phoneNum
|
||||||
|
req.PhoneNumberSet = [newphoneNum]
|
||||||
|
req.SessionContext = ""
|
||||||
|
req.ExtendCode = ""
|
||||||
|
req.SenderId = ""
|
||||||
|
resp = client.SendSms(req)
|
||||||
|
print(resp.to_json_string(indent=2))
|
||||||
|
except TencentCloudSDKException as err:
|
||||||
|
print(err)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def sengTgmesage(message):
|
||||||
|
url = "http://192.168.31.38:8521/send_message"
|
||||||
|
data = {"message": message}
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
try:
|
||||||
|
response_data = response.json()
|
||||||
|
print(response_data)
|
||||||
|
except requests.exceptions.JSONDecodeError:
|
||||||
|
print("Response content is not in JSON format")
|
||||||
|
print("Response content:", response.text)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/hello")
|
||||||
|
def hello():
|
||||||
|
# response = f"你好,\n..........................\n一名开发者\n副业:跨境电商\n#公众号:异度世界\n..........................\n回复关键词,了解更多:\n1. 微信机器人\n2. 数字游民\n3. 加群\n..........................\n[该消息是接受好友自动回复]"
|
||||||
|
response = f"你好,\n..........................\n一名开发者\n副业:跨境电商\n#公众号:异度世界\n..........................\n回复关键词,了解更多:\n1. 数字游民\n..........................\n[该消息是接受好友自动回复]"
|
||||||
|
|
||||||
|
return PlainTextResponse(content=response)
|
||||||
|
|
||||||
|
@app.post("/payh5")
|
||||||
|
async def payh5(item: dict):
|
||||||
|
print('item', item)
|
||||||
|
# 时间戳
|
||||||
|
timenew = str(int(time.time()))
|
||||||
|
order = {
|
||||||
|
"mchid": mchid,
|
||||||
|
"body": timenew, # 订单标题
|
||||||
|
"out_trade_no": timenew, # 订单号
|
||||||
|
"total_fee":item['total_fee'], # 金额,单位:分
|
||||||
|
# "openid": "o7LFAwWu3OhSrs49-1x5cSlFm0D8", # 通过openid接口获取到的openid
|
||||||
|
# "notify_url": ""
|
||||||
|
# "callback_url":'https://aiimg.hackrobot.cn/api'
|
||||||
|
}
|
||||||
|
|
||||||
|
order["callback_url"] =item['callback_url']
|
||||||
|
order["openid"] =item['openid']
|
||||||
|
order["sign"] = sign(order)
|
||||||
|
|
||||||
|
request_url = "https://payjs.cn/api/jsapi"
|
||||||
|
# 返回结果中包含`jsapi`字段,该字段的值即是前端发起时所需的6个支付参数
|
||||||
|
headers = {"content-type": "application/x-www-form-urlencoded"}
|
||||||
|
response = requests.post(request_url, data=order, headers=headers)
|
||||||
|
if response:
|
||||||
|
print(response.json())
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/tgMessage")
|
||||||
|
def tgMessagefun(openid: Optional[str] = None, db: Session = Depends(get_db)):
|
||||||
|
current_time = datetime.now()
|
||||||
|
# 格式化日期和时间,不包含空格
|
||||||
|
formatted_now = current_time.strftime("%Y%m%d%H%M%S")
|
||||||
|
|
||||||
|
print("Current Date and Time:", formatted_now)
|
||||||
|
# 发送tg消息通知 wxid_4413224132412 || o7LFAwR32hWGq9XOpd7ZxK1wZxq8
|
||||||
|
|
||||||
|
# openid = "o7LFAwR32hWGq9XOpd7ZxK1wZxq8"
|
||||||
|
print('openid', openid)
|
||||||
|
if openid:
|
||||||
|
# 查询用户信息:昵称
|
||||||
|
data = read_user(openid, db=db)
|
||||||
|
# print('data==>', data)
|
||||||
|
userInfo = data['userInfo']
|
||||||
|
# print('userInfo==>', userInfo)
|
||||||
|
if userInfo:
|
||||||
|
# print('userInfo==>', userInfo)
|
||||||
|
# message = f"{userInfo['nickname']}/{userInfo['gender']}在{current_time}进行了访问"
|
||||||
|
message = f"{userInfo.nickname}/{userInfo.gender}在{formatted_now}进行了访问"
|
||||||
|
print(message)
|
||||||
|
sengTgmesage(message)
|
||||||
|
|
||||||
|
# tgMessage.sendTg(message)
|
||||||
|
else:
|
||||||
|
print('openid未关联')
|
||||||
|
message = f'openid: {openid}-未关联,在{formatted_now}访问网页'
|
||||||
|
print(message)
|
||||||
|
sengTgmesage(message)
|
||||||
|
|
||||||
|
# tgMessage.sendTg(message)
|
||||||
|
|
||||||
|
|
||||||
|
def create_user_task(db: Session, user: schemas.UserCreate):
|
||||||
|
try:
|
||||||
|
existing_user = db.query(
|
||||||
|
models.User).filter(models.User.wxid == user.wxid).first()
|
||||||
|
if existing_user:
|
||||||
|
# 可以选择更新用户信息或返回错误
|
||||||
|
print("用户已存在create_user_task")
|
||||||
|
return "用户已存在"
|
||||||
|
else:
|
||||||
|
user = models.User(nickname=user.nickname,
|
||||||
|
remark=user.remark,
|
||||||
|
wechat_number=user.wechat_number,
|
||||||
|
wxid=user.wxid,
|
||||||
|
gender=user.gender,
|
||||||
|
friendaddtime=user.friendaddtime,
|
||||||
|
openid=user.openid)
|
||||||
|
db.add(user)
|
||||||
|
db.commit()
|
||||||
|
db.refresh(user)
|
||||||
|
|
||||||
|
except IntegrityError:
|
||||||
|
db.rollback()
|
||||||
|
print("IntegrityError")
|
||||||
|
return "IntegrityError"
|
||||||
|
|
||||||
|
|
||||||
|
# 生成二维码图片
|
||||||
|
def generateqr(url="http://nomad.hackrobot.cn",
|
||||||
|
wxid=None,
|
||||||
|
group_image_path="images/group.jpg"):
|
||||||
|
|
||||||
|
# url = "http://nomad.hackrobot.cn"
|
||||||
|
# url="http://192.168.31.219:10086"
|
||||||
|
if wxid:
|
||||||
|
url += f"?wxid={wxid}"
|
||||||
|
|
||||||
|
# 创建二维码
|
||||||
|
qr = qrcode.QRCode(version=1, box_size=5, border=2) # 调整二维码大小
|
||||||
|
qr.add_data(url)
|
||||||
|
qr.make(fit=True)
|
||||||
|
img = qr.make_image(fill='black', back_color='white')
|
||||||
|
|
||||||
|
# 保存二维码图像
|
||||||
|
qr_image_path = f"images/{wxid}-qr_code.png"
|
||||||
|
img.save(qr_image_path)
|
||||||
|
|
||||||
|
# 加载原图
|
||||||
|
# group_image_path = "images/group.jpg"
|
||||||
|
|
||||||
|
# 在原图上添加二维码(右下角)
|
||||||
|
group_image = Image.open(group_image_path)
|
||||||
|
qr_image = Image.open(qr_image_path)
|
||||||
|
|
||||||
|
# 将二维码放置在右下角
|
||||||
|
qr_image = qr_image.resize((200, 200)) # 调整二维码大小
|
||||||
|
qr_position = (group_image.width - qr_image.width - 50,
|
||||||
|
group_image.height - qr_image.height - 50) # 距离20像素
|
||||||
|
# 在原图上添加二维码
|
||||||
|
group_image.paste(qr_image, qr_position)
|
||||||
|
current_time = datetime.now()
|
||||||
|
# 格式化日期和时间,不包含空格
|
||||||
|
formatted_now = current_time.strftime("%Y%m%d%H%M%S")
|
||||||
|
# 保存合成的图像
|
||||||
|
# combined_image_path = f"images/combined_{wxid}_{datetime.now()}.jpg"
|
||||||
|
combined_image_path = f"images/combined_{wxid}_{formatted_now}.jpg"
|
||||||
|
|
||||||
|
group_image.save(combined_image_path)
|
||||||
|
# 压缩图片
|
||||||
|
group_image.save(combined_image_path, "JPEG",
|
||||||
|
quality=10) # 85 是压缩质量,可根据需要调整
|
||||||
|
|
||||||
|
# 删除二维码文件
|
||||||
|
os.remove(qr_image_path)
|
||||||
|
print(combined_image_path)
|
||||||
|
return combined_image_path
|
||||||
|
# return FileResponse(combined_image_path)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/add")
|
||||||
|
def add_user(nickname: Optional[Any] = None,
|
||||||
|
remark: Optional[Any] = None,
|
||||||
|
wechat_number: Optional[Any] = None,
|
||||||
|
wxid: Optional[Any] = None,
|
||||||
|
gender: Optional[Any] = None,
|
||||||
|
friendaddtime: Optional[Any] = None,
|
||||||
|
openid: Optional[Any] = None,
|
||||||
|
keyword: Optional[Any] = None,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
background_tasks: BackgroundTasks = None):
|
||||||
|
response = None
|
||||||
|
user_data = schemas.UserCreate(
|
||||||
|
nickname=nickname,
|
||||||
|
remark=remark,
|
||||||
|
wechat_number=wechat_number,
|
||||||
|
wxid=wxid,
|
||||||
|
gender=gender,
|
||||||
|
# friendaddtime=friendaddtime,
|
||||||
|
friendaddtime=str(datetime.now().strftime("%Y%m%d%H%M%S")),
|
||||||
|
openid=openid)
|
||||||
|
if background_tasks:
|
||||||
|
background_tasks.add_task(create_user_task, db, user_data)
|
||||||
|
# response = f"{nickname}你好,\n..........................\n一名开发者\n副业:跨境电商\n#公众号:异度世界\n..........................\n回复关键词,了解更多:\n1. 微信机器人\n2. 数字游民\n3. 加群\n..........................\n[该消息是接受好友自动回复]"
|
||||||
|
if gender=="女":
|
||||||
|
response = f"{nickname},你好:\n..........................\n一名开发者\n副业:youtube/跨境电商\n#公众号:异度世界\n..........................\n参加沙龙:\nnomad.hackrobot.cn\n..........................\n[本消息是🤖接受好友自动回复]\n我会不定时看消息"
|
||||||
|
else:
|
||||||
|
# ..........................\n回复关键词: 加群\n
|
||||||
|
response = f"{nickname},你好:\n..........................\n一名开发者\n副业:youtube/跨境电商\n#公众号:异度世界\n..........................\n加群:\nbot.hackrobot.cn\n..........................\n[本消息是🤖接受好友自动回复]\n我会不定时看消息"
|
||||||
|
# 关键词回复
|
||||||
|
if keyword == '加群' or keyword == "数字游民" or keyword == "3" or keyword == "1":
|
||||||
|
print("keyword==加群")
|
||||||
|
combined_image_path = generateqr(url="http://nomad.hackrobot.cn",
|
||||||
|
wxid=wxid,
|
||||||
|
group_image_path="images/nomadqr.jpg")
|
||||||
|
return FileResponse(combined_image_path)
|
||||||
|
elif keyword == '手册':
|
||||||
|
print("keyword==手册")
|
||||||
|
response = f"手册正在编写中"
|
||||||
|
combined_image_path = generateqr(url="http://nomad.hackrobot.cn",
|
||||||
|
wxid=wxid,
|
||||||
|
group_image_path="images/bookqr.jpg")
|
||||||
|
return FileResponse(combined_image_path)
|
||||||
|
elif keyword == '活动':
|
||||||
|
print("keyword==活动")
|
||||||
|
response = f"活动正在筹备中"
|
||||||
|
combined_image_path = generateqr(
|
||||||
|
url="http://nomad.hackrobot.cn",
|
||||||
|
wxid=wxid,
|
||||||
|
group_image_path="images/meetupqr.jpg")
|
||||||
|
return FileResponse(combined_image_path)
|
||||||
|
elif keyword == 'qrcode':
|
||||||
|
print("keyword==qrcode")
|
||||||
|
combined_image_path = generateqr(
|
||||||
|
url="http://nomad.hackrobot.cn",
|
||||||
|
# wxid='qrcode',
|
||||||
|
group_image_path="images/nomadqr.jpg")
|
||||||
|
return FileResponse(combined_image_path)
|
||||||
|
else:
|
||||||
|
print('其他keyword')
|
||||||
|
print('response',response)
|
||||||
|
return PlainTextResponse(content=response)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/users/")
|
||||||
|
def read_users(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
|
||||||
|
return crud.get_users(db=db, skip=skip, limit=limit)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/user/{identifier}")
|
||||||
|
def read_user(identifier: str, db: Session = Depends(get_db)):
|
||||||
|
user = crud.get_user_by_openid_or_wxid(db, identifier=identifier)
|
||||||
|
if user is None:
|
||||||
|
# raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
print('查询用户不存在')
|
||||||
|
return {"code": '400', "userInfo": None}
|
||||||
|
# return user
|
||||||
|
return {"code": '200', "userInfo": user}
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/user/{user_id}")
|
||||||
|
def delete_user(user_id: int, db: Session = Depends(get_db)):
|
||||||
|
user = crud.get_user(db=db, user_id=user_id)
|
||||||
|
if user is None:
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
return crud.delete_user(db=db, user=user)
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/user/{identifier}")
|
||||||
|
def update_user(identifier: str,
|
||||||
|
user_update: schemas.UserUpdate,
|
||||||
|
db: Session = Depends(get_db)):
|
||||||
|
user = crud.get_user_by_openid_or_wxid(db, identifier=identifier)
|
||||||
|
if user is None:
|
||||||
|
# raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
# return user
|
||||||
|
return {"code": '400', "userInfo": None}
|
||||||
|
|
||||||
|
# return crud.update_user(db=db, user=user, user_update=user_update)
|
||||||
|
return {
|
||||||
|
"code": '200',
|
||||||
|
"userInfo": crud.update_user(db=db, user=user, user_update=user_update)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 获取手机号=>生成验证码
|
||||||
|
@app.post("/get_verification_code/")
|
||||||
|
def get_verification_code(wxid: str,
|
||||||
|
phone_number: str,
|
||||||
|
db: Session = Depends(get_db)):
|
||||||
|
verification_code = '{:04d}'.format(random.randint(0, 9999))
|
||||||
|
user = db.query(models.User).filter(models.User.wxid == wxid).first()
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
|
||||||
|
user.phone_number = phone_number
|
||||||
|
user.verification_code = verification_code
|
||||||
|
db.commit()
|
||||||
|
sendSms(phone_number)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"message": "Verification code sent",
|
||||||
|
"verification_code": verification_code
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 验证码校验
|
||||||
|
@app.post("/verify_code/")
|
||||||
|
def verify_code(phone_number: str, code: str, db: Session = Depends(get_db)):
|
||||||
|
user = db.query(
|
||||||
|
models.User).filter(models.User.phone_number == phone_number).first()
|
||||||
|
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(status_code=404, detail="User not found")
|
||||||
|
|
||||||
|
if user.verification_code == code:
|
||||||
|
return {"message": "Verification successful"}
|
||||||
|
else:
|
||||||
|
raise HTTPException(status_code=400,
|
||||||
|
detail="Invalid verification code")
|
||||||
79
app/models.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
from sqlalchemy import Boolean, Column, Integer, String
|
||||||
|
from app.database import Base
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
from sqlalchemy import event
|
||||||
|
from sqlalchemy.orm.attributes import get_history
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = "users"
|
||||||
|
|
||||||
|
# id = Column(Integer, primary_key=True, index=True)
|
||||||
|
# nickname = Column(String, index=True)
|
||||||
|
# remark = Column(String)
|
||||||
|
# wechat_number = Column(String)
|
||||||
|
# wxid = Column(String, index=True, unique=True)
|
||||||
|
# gender = Column(String)
|
||||||
|
# friendaddtime = Column(String)
|
||||||
|
# openid = Column(String, index=True)
|
||||||
|
# ebook = Column(Boolean, default=False) # 新增电子书字段
|
||||||
|
# activity_status = Column(String, default="0") # 新增活动报名字段
|
||||||
|
# vip_member = Column(Boolean, default=False) # 新增VIP会员字段
|
||||||
|
# verification_code = Column(String, index=True) # 新增验证码字段
|
||||||
|
# phone_number = Column(String, index=True, unique=True) # 新增手机号字段
|
||||||
|
|
||||||
|
# 迁移mysql
|
||||||
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
nickname = Column(String(255), index=True) # 添加长度限制
|
||||||
|
remark = Column(String(255)) # 你也可以为其他字符串列添加长度限制
|
||||||
|
wechat_number = Column(String(255))
|
||||||
|
wxid = Column(String(255), index=True, unique=True)
|
||||||
|
gender = Column(String(10)) # 可以考虑为性别列添加长度限制
|
||||||
|
friendaddtime = Column(String(50)) # 你也可以为日期时间字段选择合适的长度
|
||||||
|
openid = Column(String(255), index=True)
|
||||||
|
ebook = Column(Boolean, default=False)
|
||||||
|
activity_status = Column(String(50)) # 为活动状态添加合适的长度
|
||||||
|
vip_member = Column(Boolean, default=False)
|
||||||
|
verification_code = Column(String(50), index=True) # 验证码的长度
|
||||||
|
phone_number = Column(String(20), index=True, unique=True) # 电话号码长度
|
||||||
|
|
||||||
|
|
||||||
|
def sengTgmesage(message):
|
||||||
|
url = "http://192.168.31.38:8521/send_message"
|
||||||
|
data = {"message":message}
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
response = requests.post(url, json=data, headers=headers)
|
||||||
|
try:
|
||||||
|
response_data = response.json()
|
||||||
|
print(response_data)
|
||||||
|
except requests.exceptions.JSONDecodeError:
|
||||||
|
print("Response content is not in JSON format")
|
||||||
|
print("Response content:", response.text)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def handle_change(mapper, connection, target):
|
||||||
|
"""监听器函数"""
|
||||||
|
if target.__class__.__name__ == 'User': # 确保只监听User类的变动
|
||||||
|
changed_fields = []
|
||||||
|
original_value=None
|
||||||
|
new_value=None
|
||||||
|
|
||||||
|
|
||||||
|
for attr in target.__mapper__.column_attrs:
|
||||||
|
history = get_history(target, attr.key)
|
||||||
|
if history.has_changes():
|
||||||
|
changed_fields.append(attr.key)
|
||||||
|
original_value = history.deleted[0] if history.deleted else None
|
||||||
|
new_value = history.added[0] if history.added else None
|
||||||
|
|
||||||
|
|
||||||
|
if changed_fields:
|
||||||
|
print(f"检测到用户 {target.nickname}-{target.remark} 的字段变化: {changed_fields}-{new_value}")
|
||||||
|
msg=f"检测到用户 {target.nickname}-{target.remark} 的字段变化: {changed_fields}-{new_value}"
|
||||||
|
sengTgmesage(msg)
|
||||||
|
# 发起HTTP请求
|
||||||
|
# requests.post("http://your-fastapi-endpoint/notify_change", json={"user_id": target.id, "changed_fields": changed_fields})
|
||||||
|
|
||||||
|
event.listen(User, "after_update", handle_change)
|
||||||
29
app/schemas.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
class UserBase(BaseModel):
|
||||||
|
nickname: Optional[str] = None
|
||||||
|
remark: Optional[str] = None
|
||||||
|
wechat_number: Optional[str] = None
|
||||||
|
wxid: Optional[str] = None
|
||||||
|
gender: Optional[str] = None
|
||||||
|
friendaddtime: Optional[str] = None
|
||||||
|
openid: Optional[str] = None
|
||||||
|
ebook: Optional[bool] = None # 新增电子书字段
|
||||||
|
activity_status: Optional[str] = None # 新增活动报名字段
|
||||||
|
vip_member: Optional[bool] = None # 新增VIP会员字段
|
||||||
|
verification_code: Optional[bool] = None #新增验证码字段
|
||||||
|
|
||||||
|
|
||||||
|
class UserCreate(UserBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class UserUpdate(UserBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class User(UserBase):
|
||||||
|
id: int
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
# orm_mode = True
|
||||||
|
from_attributes = True
|
||||||
123
app/tentcentSMS.py
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# pip install --upgrade tencentcloud-sdk-python
|
||||||
|
|
||||||
|
from tencentcloud.common import credential
|
||||||
|
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
|
||||||
|
# 导入对应产品模块的client models。
|
||||||
|
from tencentcloud.sms.v20210111 import sms_client, models
|
||||||
|
|
||||||
|
# github地址 https://github.com/TencentCloud/tencentcloud-sdk-python
|
||||||
|
# pip install --upgrade tencentcloud-sdk-python
|
||||||
|
|
||||||
|
# 导入可选配置类
|
||||||
|
from tencentcloud.common.profile.client_profile import ClientProfile
|
||||||
|
from tencentcloud.common.profile.http_profile import HttpProfile
|
||||||
|
import random
|
||||||
|
|
||||||
|
|
||||||
|
# phoneNum="+8613243889782"
|
||||||
|
def sendSms(phoneNum,verification_code=None):
|
||||||
|
try:
|
||||||
|
# 必要步骤:
|
||||||
|
# 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。
|
||||||
|
# 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。
|
||||||
|
# 你也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人,
|
||||||
|
# 以免泄露密钥对危及你的财产安全。
|
||||||
|
# SecretId、SecretKey 查询: https://console.cloud.tencent.com/cam/capi
|
||||||
|
cred = credential.Credential("AKIDOaT6SZQ0JL7cqnTryoOhkW1FBqELAizz",
|
||||||
|
"kDPKyMTJs99sKVP1NREkzxKQFo66pIUP")
|
||||||
|
# cred = credential.Credential(
|
||||||
|
# os.environ.get(""),
|
||||||
|
# os.environ.get("")
|
||||||
|
# )
|
||||||
|
|
||||||
|
# 实例化一个http选项,可选的,没有特殊需求可以跳过。
|
||||||
|
httpProfile = HttpProfile()
|
||||||
|
# 如果需要指定proxy访问接口,可以按照如下方式初始化hp(无需要直接忽略)
|
||||||
|
# httpProfile = HttpProfile(proxy="http://用户名:密码@代理IP:代理端口")
|
||||||
|
httpProfile.reqMethod = "POST" # post请求(默认为post请求)
|
||||||
|
httpProfile.reqTimeout = 30 # 请求超时时间,单位为秒(默认60秒)
|
||||||
|
httpProfile.endpoint = "sms.tencentcloudapi.com" # 指定接入地域域名(默认就近接入)
|
||||||
|
|
||||||
|
# 非必要步骤:
|
||||||
|
# 实例化一个客户端配置对象,可以指定超时时间等配置
|
||||||
|
clientProfile = ClientProfile()
|
||||||
|
clientProfile.signMethod = "TC3-HMAC-SHA256" # 指定签名算法
|
||||||
|
clientProfile.language = "en-US"
|
||||||
|
# clientProfile.language = "en-ZH"
|
||||||
|
clientProfile.httpProfile = httpProfile
|
||||||
|
|
||||||
|
# 实例化要请求产品(以sms为例)的client对象
|
||||||
|
# 第二个参数是地域信息,可以直接填写字符串ap-guangzhou,支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8
|
||||||
|
client = sms_client.SmsClient(cred, "ap-beijing", clientProfile)
|
||||||
|
|
||||||
|
# 实例化一个请求对象,根据调用的接口和实际情况,可以进一步设置请求参数
|
||||||
|
# 你可以直接查询SDK源码确定SendSmsRequest有哪些属性可以设置
|
||||||
|
# 属性可能是基本类型,也可能引用了另一个数据结构
|
||||||
|
# 推荐使用IDE进行开发,可以方便的跳转查阅各个接口和数据结构的文档说明
|
||||||
|
req = models.SendSmsRequest()
|
||||||
|
|
||||||
|
# 基本类型的设置:
|
||||||
|
# SDK采用的是指针风格指定参数,即使对于基本类型你也需要用指针来对参数赋值。
|
||||||
|
# SDK提供对基本类型的指针引用封装函数
|
||||||
|
# 帮助链接:
|
||||||
|
# 短信控制台: https://console.cloud.tencent.com/smsv2
|
||||||
|
# 腾讯云短信小助手: https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81
|
||||||
|
|
||||||
|
# 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId,示例如1400006666
|
||||||
|
# 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
|
||||||
|
req.SmsSdkAppId = "1400808237"
|
||||||
|
# 短信签名内容: 使用 UTF-8 编码,必须填写已审核通过的签名
|
||||||
|
# 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
|
||||||
|
req.SignName = "异度世界公众号"
|
||||||
|
# 模板 ID: 必须填写已审核通过的模板 ID
|
||||||
|
# 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
|
||||||
|
# req.TemplateId = "1976081" #口令:3399,将数字复制,发送到机器人(hackrobot),会自动加
|
||||||
|
req.TemplateId = "1795415" #验证码为:{1}, 若非本人操作,请忽略。
|
||||||
|
|
||||||
|
# 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致,,若无模板参数,则设置为空
|
||||||
|
# 随机4位数字
|
||||||
|
# verification_code = '{:04d}'.format(random.randint(0, 9999))
|
||||||
|
req.TemplateParamSet = [verification_code]
|
||||||
|
# 下发手机号码,采用 E.164 标准,+[国家或地区码][手机号]
|
||||||
|
# 示例如:+8613711112222, 其中前面有一个+号 ,86为国家码,13711112222为手机号,最多不要超过200个手机号
|
||||||
|
newphoneNum = '+86' + phoneNum
|
||||||
|
req.PhoneNumberSet = [newphoneNum]
|
||||||
|
# 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回
|
||||||
|
req.SessionContext = ""
|
||||||
|
# 短信码号扩展号(无需要可忽略): 默认未开通,如需开通请联系 [腾讯云短信小助手]
|
||||||
|
req.ExtendCode = ""
|
||||||
|
# 国内短信无需填写该项;国际/港澳台短信已申请独立 SenderId 需要填写该字段,默认使用公共 SenderId,无需填写该字段。注:月度使用量达到指定量级可申请独立 SenderId 使用,详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。
|
||||||
|
req.SenderId = ""
|
||||||
|
|
||||||
|
resp = client.SendSms(req)
|
||||||
|
|
||||||
|
# 输出json格式的字符串回包`
|
||||||
|
print(resp.to_json_string(indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
# {
|
||||||
|
# "SendStatusSet": [
|
||||||
|
# {
|
||||||
|
# "SerialNo": "4412:418220390917215411841458978",
|
||||||
|
# "PhoneNumber": "+8613243889782",
|
||||||
|
# "Fee": 1,
|
||||||
|
# "SessionContext": "",
|
||||||
|
# "Code": "Ok",
|
||||||
|
# "Message": "send success",
|
||||||
|
# "IsoCode": "CN"
|
||||||
|
# }
|
||||||
|
# ],
|
||||||
|
# "RequestId": "5c23933c-df3d-4fd7-9445-1e5c7745492d"
|
||||||
|
# }
|
||||||
|
|
||||||
|
# 当出现以下错误码时,快速解决方案参考
|
||||||
|
# - [FailedOperation.SignatureIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.signatureincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
|
||||||
|
# - [FailedOperation.TemplateIncorrectOrUnapproved](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Afailedoperation.templateincorrectorunapproved-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
|
||||||
|
# - [UnauthorizedOperation.SmsSdkAppIdVerifyFail](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunauthorizedoperation.smssdkappidverifyfail-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
|
||||||
|
# - [UnsupportedOperation.ContainDomesticAndInternationalPhoneNumber](https://cloud.tencent.com/document/product/382/9558#.E7.9F.AD.E4.BF.A1.E5.8F.91.E9.80.81.E6.8F.90.E7.A4.BA.EF.BC.9Aunsupportedoperation.containdomesticandinternationalphonenumber-.E5.A6.82.E4.BD.95.E5.A4.84.E7.90.86.EF.BC.9F)
|
||||||
|
# - 更多错误,可咨询[腾讯云助手](https://tccc.qcloud.com/web/im/index.html#/chat?webAppId=8fa15978f85cb41f7e2ea36920cb3ae1&title=Sms)
|
||||||
|
|
||||||
|
except TencentCloudSDKException as err:
|
||||||
|
print(err)
|
||||||
|
|
||||||
16
app/tgMessage.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import time
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
# bot发消息到tg群组
|
||||||
|
def sendTg(message):
|
||||||
|
baseUrl="https://api.telegram.org/bot"
|
||||||
|
toekn="558659722:AAENA3v8gKq7R7_nsr8V58Iu-_Z6BWx2SCw"
|
||||||
|
chat_id="-861577609"
|
||||||
|
text=message
|
||||||
|
url=f'{baseUrl}{toekn}/sendMessage?chat_id={chat_id}&text={text}'
|
||||||
|
try:
|
||||||
|
r = requests.get(url)
|
||||||
|
except:
|
||||||
|
print('请检查网络代理')
|
||||||
|
# https://api.telegram.org/bot558659722:AAENA3v8gKq7R7_nsr8V58Iu-_Z6BWx2SCw/sendMessage?chat_id=-861577609&text=1234
|
||||||
BIN
images/111.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
images/bookqr.jpg
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
images/combined_.jpg
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
images/combined_222.jpg
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
images/combined_None.jpg
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
images/combined_qrcode.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
images/combined_wxid_4413224132412.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
images/combined_wxid_4413224132412.png
Normal file
|
After Width: | Height: | Size: 611 KiB |
BIN
images/combined_wxid_4413224132412_20240721070722.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
images/combined_wxid_4413224132412_20240721070818.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
images/combined_wxid_4413224132412_20240721070840.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
images/combined_wxid_d5cny7otsd1922.jpg
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
images/combined_wxid_o0ktftgecfde22.jpg
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
images/group.jpg
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
images/group2.jpg
Normal file
|
After Width: | Height: | Size: 324 KiB |
BIN
images/meetupqr.jpg
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
images/nomadqr.jpg
Normal file
|
After Width: | Height: | Size: 102 KiB |
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
fastapi
|
||||||
|
uvicorn
|
||||||
|
sqlalchemy
|
||||||
|
pydantic
|
||||||
6
run.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import uvicorn
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# uvicorn.run("app.main:app", host="0.0.0.0", port=8700, reload=True)
|
||||||
|
uvicorn.run("app.main:app", host="127.0.0.1", port=8700, reload=True)
|
||||||
|
|
||||||
26
test
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: mysql_db
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: Xiao4669805
|
||||||
|
MYSQL_DATABASE: hackrobot
|
||||||
|
MYSQL_USER: hackrobot
|
||||||
|
MYSQL_PASSWORD: Xiao4669805
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
volumes:
|
||||||
|
- mysql_data:/var/lib/mysql
|
||||||
|
|
||||||
|
adminer:
|
||||||
|
image: adminer
|
||||||
|
container_name: adminer
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
restart: always # 为 Adminer 容器添加自动重启配置
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql_data:
|
||||||
|
driver: local
|
||||||