初始化
This commit is contained in:
commit
6283ea4afd
3
.env
Normal file
3
.env
Normal file
@ -0,0 +1,3 @@
|
||||
DATABASE_URL=mysql+pymysql://acore:acore@127.0.0.1:3316/sm_cdk
|
||||
FLASK_ENV=development
|
||||
FLASK_APP=app.py
|
||||
35
README.md
Normal file
35
README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# CDK宣传系统
|
||||
|
||||
这是一个用于管理游戏宣传和CDK发放的Web应用系统。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 确保您的电脑已安装 Python 3.x 版本
|
||||
- 如果没有安装,请从 https://www.python.org/downloads/ 下载并安装
|
||||
- 安装时请勾选"Add Python to PATH"选项
|
||||
|
||||
2. 确保MySQL服务已启动,并且配置正确
|
||||
- 数据库名称:sm_cdk
|
||||
- 用户名:acore
|
||||
- 密码:acore
|
||||
- 端口:3316
|
||||
|
||||
3. 启动系统
|
||||
- 双击运行 `start.bat`
|
||||
- 等待系统启动完成
|
||||
- 在浏览器中访问:http://127.0.0.1:5000
|
||||
|
||||
4. 使用说明
|
||||
- 在"生成宣传内容"页面可以选择使用游戏角色名或游戏账号生成宣传内容
|
||||
- 生成的内容可以一键复制
|
||||
- 发布宣传内容后,在CDK领取页面提交链接即可获取CDK
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 每个角色/账号只能领取一次CDK
|
||||
2. 请确保完整复制并发布宣传内容
|
||||
3. 发布链接必须可以公开访问
|
||||
4. 如遇到问题,请检查:
|
||||
- MySQL服务是否正常运行
|
||||
- 数据库连接信息是否正确
|
||||
- 网络连接是否正常
|
||||
235
app.py
Normal file
235
app.py
Normal file
@ -0,0 +1,235 @@
|
||||
from flask import Flask, render_template, request, jsonify, flash, redirect, url_for
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
import random
|
||||
import string
|
||||
import pymysql
|
||||
import re
|
||||
from config import AD_TEMPLATE, SERVER_INFO, VERIFICATION_CONFIG, NAME_TYPES
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
# 设置PyMySQL作为MySQL驱动
|
||||
pymysql.install_as_MySQLdb()
|
||||
|
||||
load_dotenv()
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# 设置 Flask secret key
|
||||
app.secret_key = os.getenv('SECRET_KEY', 'dev_key_123') # 在生产环境中使用环境变量设置
|
||||
|
||||
# 数据库配置
|
||||
database_url = os.getenv('DATABASE_URL')
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = database_url
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
|
||||
# 创建数据库连接
|
||||
def create_database():
|
||||
try:
|
||||
# 解析数据库URL
|
||||
url_parts = database_url.split('/')
|
||||
db_name = url_parts[-1]
|
||||
base_url = '/'.join(url_parts[:-1])
|
||||
|
||||
# 创建数据库引擎(不指定数据库名)
|
||||
engine = create_engine(base_url)
|
||||
|
||||
# 连接并创建数据库
|
||||
with engine.connect() as conn:
|
||||
conn.execute(f"CREATE DATABASE IF NOT EXISTS {db_name}")
|
||||
print(f"数据库 {db_name} 创建成功")
|
||||
except Exception as e:
|
||||
print(f"创建数据库失败:{str(e)}")
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
# 定义CDK模型
|
||||
class CDKCode(db.Model):
|
||||
__tablename__ = '兑换码'
|
||||
ID = db.Column(db.Integer, primary_key=True)
|
||||
兑换码 = db.Column(db.String(20), unique=True, nullable=False)
|
||||
奖励模板ID = db.Column(db.Integer, nullable=False)
|
||||
|
||||
# 定义已使用的CDK记录
|
||||
class UsedCDK(db.Model):
|
||||
__tablename__ = 'used_cdks'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
character_name = db.Column(db.String(50), nullable=False)
|
||||
post_url = db.Column(db.String(255), nullable=False)
|
||||
cdk = db.Column(db.String(20), nullable=False)
|
||||
used_at = db.Column(db.DateTime, server_default=db.func.now())
|
||||
|
||||
def verify_ad_content(content, character_name):
|
||||
# 检查必须包含的关键词
|
||||
for keyword in VERIFICATION_CONFIG['required_keywords']:
|
||||
if keyword not in content:
|
||||
return False
|
||||
|
||||
# 检查必须匹配的模式
|
||||
for pattern in VERIFICATION_CONFIG['required_patterns']:
|
||||
if not re.search(pattern, content):
|
||||
return False
|
||||
|
||||
# 确保包含角色名
|
||||
if character_name not in content:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/generate')
|
||||
def generate():
|
||||
return render_template('generate.html', server_info=SERVER_INFO)
|
||||
|
||||
@app.route('/create-ad', methods=['POST'])
|
||||
def create_ad():
|
||||
character_name = request.form.get('character_name')
|
||||
name_type = request.form.get('name_type', 'character')
|
||||
|
||||
if not character_name:
|
||||
return jsonify({'success': False, 'message': '请输入名称'})
|
||||
|
||||
ad_content = AD_TEMPLATE.format(
|
||||
server_name=SERVER_INFO['server_name'],
|
||||
name_type=NAME_TYPES[name_type],
|
||||
name=character_name,
|
||||
server_features=SERVER_INFO['server_features'],
|
||||
website=SERVER_INFO['website']
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'ad_content': ad_content
|
||||
})
|
||||
|
||||
@app.route('/verify', methods=['POST'])
|
||||
def verify_post():
|
||||
character_name = request.form.get('character_name')
|
||||
post_url = request.form.get('post_url')
|
||||
|
||||
if not character_name or not post_url:
|
||||
return jsonify({'success': False, 'message': '请填写所有必要信息'})
|
||||
|
||||
# 检查该角色是否已经领取过CDK
|
||||
used = UsedCDK.query.filter_by(character_name=character_name).first()
|
||||
if used:
|
||||
return jsonify({'success': False, 'message': '该角色已经领取过CDK'})
|
||||
|
||||
try:
|
||||
# 获取帖子内容
|
||||
response = requests.get(post_url)
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
post_content = soup.get_text()
|
||||
|
||||
# 验证广告内容
|
||||
if not verify_ad_content(post_content, character_name):
|
||||
return jsonify({'success': False, 'message': '未找到有效的宣传内容,请确保完整复制宣传模板'})
|
||||
|
||||
# 获取未使用的CDK
|
||||
cdk = CDKCode.query.filter(
|
||||
~CDKCode.兑换码.in_(
|
||||
db.session.query(UsedCDK.cdk)
|
||||
)
|
||||
).first()
|
||||
|
||||
if not cdk:
|
||||
return jsonify({'success': False, 'message': 'CDK已经用完,请联系管理员'})
|
||||
|
||||
# 记录使用情况
|
||||
used_cdk = UsedCDK(
|
||||
character_name=character_name,
|
||||
post_url=post_url,
|
||||
cdk=cdk.兑换码
|
||||
)
|
||||
db.session.add(used_cdk)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': '验证成功!',
|
||||
'cdk': cdk.兑换码
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'message': f'验证失败:{str(e)}'})
|
||||
|
||||
@app.route('/redeem', methods=['GET', 'POST'])
|
||||
def redeem():
|
||||
if request.method == 'POST':
|
||||
post_url = request.form.get('link')
|
||||
if not post_url:
|
||||
flash('请输入宣传链接', 'danger')
|
||||
return redirect(url_for('redeem'))
|
||||
|
||||
try:
|
||||
response = requests.get(post_url)
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
post_content = soup.get_text()
|
||||
|
||||
# 使用正则表达式提取角色名
|
||||
character_match = re.search(r'推荐(角色|账号):([^\n\r]+)', post_content)
|
||||
if not character_match:
|
||||
flash('未在帖子中找到有效的推荐信息', 'danger')
|
||||
return redirect(url_for('redeem'))
|
||||
|
||||
character_name = character_match.group(2).strip()
|
||||
|
||||
# 验证广告内容
|
||||
if not verify_ad_content(post_content, character_name):
|
||||
flash('未找到有效的宣传内容,请确保完整复制宣传模板', 'danger')
|
||||
return redirect(url_for('redeem'))
|
||||
|
||||
# 检查是否已经领取过
|
||||
used = UsedCDK.query.filter_by(character_name=character_name).first()
|
||||
if used:
|
||||
flash('该角色已经领取过CDK', 'danger')
|
||||
return redirect(url_for('redeem'))
|
||||
|
||||
# 获取未使用的CDK
|
||||
cdk = CDKCode.query.filter(
|
||||
~CDKCode.兑换码.in_(
|
||||
db.session.query(UsedCDK.cdk)
|
||||
)
|
||||
).first()
|
||||
|
||||
if not cdk:
|
||||
flash('CDK已经用完,请联系管理员', 'danger')
|
||||
return redirect(url_for('redeem'))
|
||||
|
||||
# 记录使用情况
|
||||
used_cdk = UsedCDK(
|
||||
character_name=character_name,
|
||||
post_url=post_url,
|
||||
cdk=cdk.兑换码
|
||||
)
|
||||
db.session.add(used_cdk)
|
||||
db.session.commit()
|
||||
|
||||
flash(f'恭喜!您的CDK是:{cdk.兑换码}', 'success')
|
||||
return redirect(url_for('redeem'))
|
||||
|
||||
except Exception as e:
|
||||
flash(f'验证失败:{str(e)}', 'danger')
|
||||
return redirect(url_for('redeem'))
|
||||
|
||||
return render_template('redeem.html', server_info=SERVER_INFO)
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 创建数据库
|
||||
create_database()
|
||||
|
||||
with app.app_context():
|
||||
try:
|
||||
# 创建所有表
|
||||
db.create_all()
|
||||
print("数据库表创建成功")
|
||||
except Exception as e:
|
||||
print(f"数据库表创建失败:{str(e)}")
|
||||
|
||||
app.run(debug=True)
|
||||
40
config.py
Normal file
40
config.py
Normal file
@ -0,0 +1,40 @@
|
||||
# 广告语模板配置
|
||||
AD_TEMPLATE = """
|
||||
{server_name} - 魔兽世界3.3.5a服务器
|
||||
{name_type}:{name}
|
||||
服务器特色:
|
||||
{server_features}
|
||||
|
||||
更多详情请访问:{website}
|
||||
"""
|
||||
|
||||
# 推荐人类型文本
|
||||
NAME_TYPES = {
|
||||
"character": "推荐角色",
|
||||
"account": "推荐账号"
|
||||
}
|
||||
|
||||
# 服务器基本信息
|
||||
SERVER_INFO = {
|
||||
"server_name": "[AzerothCore]",
|
||||
"server_features": """1. 稳定流畅的游戏体验
|
||||
2. 专业的技术团队支持
|
||||
3. 独特的游戏玩法
|
||||
4. 丰富的游戏内容""",
|
||||
"website": "www.example.com"
|
||||
}
|
||||
|
||||
# 验证配置
|
||||
VERIFICATION_CONFIG = {
|
||||
# 必须包含的关键词
|
||||
"required_keywords": [
|
||||
"魔兽世界3.3.5a",
|
||||
"服务器特色",
|
||||
"游戏体验"
|
||||
],
|
||||
# 广告语中必须包含的部分(正则表达式)
|
||||
"required_patterns": [
|
||||
r"推荐(角色|账号):.*?[\n\r]", # 确保包含推荐人信息
|
||||
r"服务器特色:[\s\S]*?游戏内容" # 确保包含服务器特色部分
|
||||
]
|
||||
}
|
||||
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Flask==2.0.1
|
||||
Flask-SQLAlchemy==2.5.1
|
||||
PyMySQL==1.0.2
|
||||
requests==2.26.0
|
||||
beautifulsoup4==4.9.3
|
||||
python-dotenv==0.19.0
|
||||
Werkzeug==2.0.1
|
||||
SQLAlchemy==1.4.23
|
||||
112
templates/generate.html
Normal file
112
templates/generate.html
Normal file
@ -0,0 +1,112 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>生成宣传内容</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="text-center">生成宣传内容</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="generateForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">推荐人类型</label>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="name_type" id="character" value="character" checked>
|
||||
<label class="form-check-label" for="character">
|
||||
游戏角色名
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="name_type" id="account" value="account">
|
||||
<label class="form-check-label" for="account">
|
||||
游戏账号
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="character_name" class="form-label" id="nameLabel">游戏角色名</label>
|
||||
<input type="text" class="form-control" id="character_name" name="character_name" required>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button type="submit" class="btn btn-primary">生成内容</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="result" class="mt-4" style="display: none;">
|
||||
<h4>生成的宣传内容[格式需保持一致]:</h4>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<pre id="generatedContent" class="mb-3"></pre>
|
||||
<button id="copyButton" class="btn btn-success">复制内容</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 text-center">
|
||||
<a href="/" class="btn btn-secondary">返回首页</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// 更新输入框标签
|
||||
document.querySelectorAll('input[name="name_type"]').forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
const label = document.getElementById('nameLabel');
|
||||
label.textContent = this.value === 'character' ? '游戏角色名' : '游戏账号';
|
||||
});
|
||||
});
|
||||
|
||||
// 处理表单提交
|
||||
document.getElementById('generateForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
const form = e.target;
|
||||
const submitButton = form.querySelector('button[type="submit"]');
|
||||
submitButton.disabled = true;
|
||||
|
||||
try {
|
||||
const formData = new FormData(form);
|
||||
const response = await fetch('/create-ad', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
document.getElementById('generatedContent').textContent = data.ad_content;
|
||||
document.getElementById('result').style.display = 'block';
|
||||
} else {
|
||||
alert(data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
alert('系统错误,请稍后再试');
|
||||
} finally {
|
||||
submitButton.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
// 复制按钮功能
|
||||
document.getElementById('copyButton').addEventListener('click', function() {
|
||||
const content = document.getElementById('generatedContent').textContent;
|
||||
navigator.clipboard.writeText(content).then(() => {
|
||||
this.textContent = '已复制';
|
||||
setTimeout(() => {
|
||||
this.textContent = '复制内容';
|
||||
}, 2000);
|
||||
}).catch(() => {
|
||||
alert('复制失败,请手动复制');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
45
templates/index.html
Normal file
45
templates/index.html
Normal file
@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>CDK兑换系统</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="text-center">CDK兑换系统</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">生成宣传内容</h5>
|
||||
<p class="card-text">生成您的专属宣传内容</p>
|
||||
<a href="/generate" class="btn btn-primary">开始生成</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title">兑换CDK</h5>
|
||||
<p class="card-text">提交宣传链接兑换CDK</p>
|
||||
<a href="/redeem" class="btn btn-success">立即兑换</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
49
templates/redeem.html
Normal file
49
templates/redeem.html
Normal file
@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>CDK兑换</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2 class="text-center">CDK兑换</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="/redeem">
|
||||
<div class="mb-3">
|
||||
<label for="link" class="form-label">宣传链接</label>
|
||||
<input type="url" class="form-control" id="link" name="link" required>
|
||||
<div class="form-text">请输入您发布宣传内容的帖子链接</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button type="submit" class="btn btn-primary">兑换CDK</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }} mt-3" role="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="mt-3 text-center">
|
||||
<a href="/" class="btn btn-secondary">返回首页</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user