宣扬华夏文化:使用 Java 开发一个 Discord 中秋节机器人
宣扬华夏文化:使用 Java 开发一个 Discord 中秋节机器人
一、背景故事
临近中秋,稀土掘金发起了《中秋创意投稿,共赏明月光芒 | 创意投稿大赛》。
我自己曾经在 Discord 搞了一个服务,里面有1万多人在里面闲聊,曾经自己也做过一个积分机器人进行服务管理。
经过我的深入思考,准备将中秋节和我的 Discord 服务结合,做一个中秋节机器人,宣扬一下我华夏千年文化,来一波文化正向输出。
Discord介绍
Discord 是一款广受欢迎的社交媒体和通信平台,由 Discord Inc. 开发和运营。Discord Inc. 是一家总部位于美国加利福尼亚州的研发公司,成立于2012年。该公司专注于开发创新的通信和社交工具,旨在为用户提供高质量的在线交流体验。
Discord 最初是为游戏社区而设计的,但现在已经成为各种兴趣和专业领域的用户群体的主要沟通平台之一。它的用户群体非常广泛,包括游戏玩家、社区组织、学生、职业人士、编程开发者和艺术家等。
二、准备结合的方式梳理
我将在中秋节机器人内置一些指令:
graph LR A(内置指令) ---> B(中秋节是什么) A(内置指令) ---> C(月饼和中秋节有什么故事) A(内置指令) ---> D(猜灯谜) A(内置指令) ---> E(榜单) A(内置指令) ---> F(积分) style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style E fill:#98FB98,stroke:#98FB98,stroke-width:2px style F fill:#ADD8E6,stroke:#ADD8E6,stroke-width:2px
2.1 了解中秋节历史文化
首先要宣扬我们的文化,所以肯定要给大家提供了解中秋的机会和方式。
2.2 月饼和中秋节的故事
了解了中秋文化肯定也要了解我们的吃文化,所以单开一个月饼和中秋节的故事指令。
2.3 中秋传统游戏猜灯谜、中秋节诗歌PK赛点、制作第一个云月饼:
要大家活跃参与我们的中秋节活动,仅仅是介绍肯定是不够的,所以这里设计了三种游戏,猜灯谜、与中秋节相关的诗歌、制作云月饼;让更多人能够参与进来。
2.4、中秋节限定积分模式
前面的了解文化,月饼,玩游戏都能获得一定的积分,这个积分是中秋节机器人限定的积分,不会和以前的积分机器人冲突。
积分规则如下:
-
了解中秋节历史文化(积10分、当次活动每人最多获得1次该积分)
-
了解月饼和中秋节的故事(积10分、当次活动每人最多获得1次该积分)
-
制作云月饼(积6分、当次活动每人最多获得1次该积分)
-
猜灯谜(猜对一次积1分、最多100积分)
-
诗歌PK(对一次积1分、无上限,但是参与积分的诗歌活动里面不能重复)
2.5 中秋节限定活动奖励
也是为了刺激大家的积极性,将设置几种限定活动奖励,包括中秋节专属荣誉称号、中秋节专属背景卡片、解锁更多服务权限。
活动奖励规则如下:
-
活动第1名获得服务管理员权限,中秋节专属背景卡片,中秋节专属荣誉称号
-
活动第2-10名获得中秋节专属背景卡片,中秋节专属荣誉称号
-
活动第11-100名获得中秋节专属荣誉称号
graph LR A(中秋节机器人) ---> B(了解中秋节历史文化) A(中秋节机器人) ---> C(月饼和中秋节的故事) A(中秋节机器人) ---> D(中秋传统游戏猜灯谜) A(中秋节机器人) ---> E(中秋节诗歌PK赛点) A(中秋节机器人) ---> F(制作第一个云月饼) A(中秋节机器人) ---> G(中秋节限定积分模式) A(中秋节机器人) ---> H(中秋节限定活动奖励) style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style E fill:#98FB98,stroke:#98FB98,stroke-width:2px style F fill:#ADD8E6,stroke:#ADD8E6,stroke-width:2px style G fill:#00FFFF,stroke:#00FFFF,stroke-width:2px style H fill:#E6E6FA,stroke:#E6E6FA,stroke-width:2px
三、实现方案
3.1 创建一个 Discord Bot
在 Discord 开发者平台创建一个新的机器人,取名为 “==中秋节机器人==” 。
注意:这里是故意取中文名称,因为本来就是为了宣扬文化嘛,几个简单的字,朋友们还是能认识的,毕竟现在的朋友几乎都能来几句国粹。
3.2 将“中秋节机器人”邀请进我们的服务(lucky star)
这里我们是直接给机器人管理权限。
3.3 实现设计
3.3.1 数据存储设计
因为“中秋节机器人”只是使用几天,而且用户也就1W+,所以所有的存储都使用内存存储。
-
文化介绍和月饼故事都直接做枚举存储
-
猜灯谜也用枚举实现
-
制作云月饼,诗歌PK不存储,只是记录参与人获得的积分
-
积分使用map结构存储
-
活动奖励当天发放到 Didcord Server 里面对应的账号(不需要存储)
3.3.2 了解中秋节历史文化
-
用户在“中秋节活动总会场”频道询问中秋节是什么。
-
中秋节机器人检测到用户的问题。
-
中秋节机器人回答用户的问题。
-
同时,中秋节机器人给用户积分10分作为奖励。
sequenceDiagram participant 用户 participant 中秋节活动总会场 participant 中秋节活动机器人 用户->>中秋节活动总会场:中秋节是什么 中秋节活动机器人->>中秋节活动总会场:监听到有人问中秋节是什么 中秋节活动机器人->>用户:告诉用户中秋节是什么 中秋节活动机器人->>用户:积10分
3.3.3 月饼和中秋节的故事
-
用户在“中秋节活动总会场”频道询问月饼和中秋节有什么故事。
-
中秋节机器人检测到用户的问题。
-
中秋节机器人回答用户的问题。
-
同时,中秋节机器人给用户积分10分作为奖励。
sequenceDiagram participant 用户 participant 中秋节活动总会场 participant 中秋节活动机器人 用户->>中秋节活动总会场:月饼和中秋节有什么故事 中秋节活动机器人->>中秋节活动总会场:监听到有人问月饼和中秋节有什么故事 中秋节活动机器人->>用户:告诉用户月饼和中秋节有什么故事 中秋节活动机器人->>用户:积10分
3.3.4 猜灯谜活动
-
用户在“中秋活动猜灯谜现场”频道说出猜灯谜三个字
-
中秋节机器人检测到猜灯谜。
-
中秋节机器人发出灯谜。
-
用户猜灯谜
-
中秋节机器人检测到有人猜对
-
中秋节机器人发出恭喜消息和积分
sequenceDiagram participant 用户 participant 中秋活动猜灯谜现场 participant 中秋节活动机器人 用户->>中秋活动猜灯谜现场:我要猜灯谜 中秋节活动机器人->>中秋活动猜灯谜现场:监听到有人说“猜灯谜” 中秋节活动机器人->>中秋活动猜灯谜现场:发出灯谜问题 用户->>中秋活动猜灯谜现场:说出答案 中秋节活动机器人->>中秋活动猜灯谜现场:监听到有人回答正确 中秋节活动机器人->>用户:恭喜用户回答正确 中秋节活动机器人->>用户:积1分
3.3.5 诗歌PK大赛
-
用户在“中秋活动诗歌现场”频道说出发送诗歌
-
中秋节机器人检测到诗词命中了中秋关键词。
-
中秋节机器人发出恭喜消息和积分
sequenceDiagram participant 用户 participant 中秋活动诗歌现场 participant 中秋节活动机器人 用户->>中秋活动诗歌现场:发出任意诗歌 中秋节活动机器人->>中秋活动诗歌现场:监听到有人诗词命中了中秋关键词 中秋节活动机器人->>用户:恭喜用户成功参与诗歌PK大赛 中秋节活动机器人->>用户:积1分
3.3.6 云月饼活动
-
用户在“中秋活动云月饼现场”频道说出想制作的月饼馅
-
中秋节机器人检测到有人说了月饼馅
-
中秋节机器人生成云月饼和积分
sequenceDiagram participant 用户 participant 中秋活动云月饼现场 participant 中秋节活动机器人 用户->>中秋活动云月饼现场:说出想制作的月饼馅 中秋节活动机器人->>中秋活动云月饼现场:测到有人说了月饼馅 中秋节活动机器人->>用户:参加中秋云月饼活动 中秋节活动机器人->>用户:积6分
3.4 一些核心代码逻辑
3.4.1 机器人监听消息互动逻辑
package com.lucky.star.midautumn.robot.listener; import com.lucky.star.midautumn.robot.enums.ChannelEnum; import com.lucky.star.midautumn.robot.enums.ScoreEnum; import com.lucky.star.midautumn.robot.service.ai.AiService; import com.lucky.star.midautumn.robot.service.score.ScoreService; import org.javacord.api.event.message.MessageCreateEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.io.ByteArrayOutputStream; /** * 用户消息监听 * * @author bamboo panda * @version 1.0 * @date 2023/9/15 10:25 */ @Component("userMessageCreateListener") public class UserMessageCreateListener extends BaseMessageCreateListener { /** * 监听到用户消息 * * @param event */ @Override public void onMessageCreate(MessageCreateEvent event) { // 忽略机器人发的消息 if (isBotMessage(event)) { return; } ChannelEnum channelType = getChannelType(event); LOGGER.info("检测到有人在{}频道发消息", channelType.name()); // 判断用户是在哪个会场参与活动 switch (channelType) { case DM: this.dm(event); break; case SG: this.sg(event); break; case YB: this.yb(event); break; case MASTER: this.master(event); break; case SCORE: this.score(event); break; default: break; } } /** * 积分频道 * * @param event */ public void score(MessageCreateEvent event) { String message = event.getMessageContent().trim(); // 用户查询积分榜单 if (message.contains("榜单")) { // 查询榜单 String ranking = scoreService.ranking(); // 发送灯谜消息 super.sendAtMessage(event, ranking); } // 用户查询个人积分 else if (message.contains("积分")) { ByteArrayOutputStream outputStream = scoreService.levelcheck(super.getSpeakUser(event)); super.sendAtMessage(event, outputStream); } } /** * 主会场活动 * * @param event */ public void master(MessageCreateEvent event) { String message = event.getMessageContent().trim(); // 智能识别是否命中主会场话题 String recognition = aiService.masterRecognition(message); // 命中了发送消息和积分 if (StringUtils.hasText(recognition)) { // 发送恭喜消息 super.sendAtMessage(event, recognition); // 积1分 scoreService.score(ChannelEnum.MASTER, super.getSpeakUser(event), ScoreEnum.TEN); } } /** * 月饼现场 * * @param event */ public void yb(MessageCreateEvent event) { String message = event.getMessageContent().trim(); // 智能识别是否符合月饼馅 String filling = aiService.yb(message); // 符合规则 if (StringUtils.hasText(filling)) { // 发送恭喜制作了一个云月饼消息 super.sendAtMessage(event, filling); // 积6分 scoreService.score(ChannelEnum.YB, super.getSpeakUser(event), ScoreEnum.SIX); } } /** * 诗歌大赛现场 * * @param event */ public void sg(MessageCreateEvent event) { String message = event.getMessageContent().trim(); // 智能识别是否符合诗歌大赛规则 Boolean success = aiService.sg(message); // 符合规则 if (success) { // 恭喜用户成功参与诗歌PK大赛 super.sendAtMessage(event, "恭喜您参加中秋诗歌大赛,恭喜您获得1积分!"); // 积1分 scoreService.score(ChannelEnum.SG, super.getSpeakUser(event), ScoreEnum.ONE); } } /** * 猜灯谜现场 * * @param event */ public void dm(MessageCreateEvent event) { String message = event.getMessageContent().trim(); // 用户发起猜灯谜 if (message.contains("猜灯谜")) { // 寻找灯谜 String riddle = aiService.sendRandomRiddle(event.getChannel()); // 发送灯谜消息 super.sendMessage(event, "猜灯谜开始!n" + riddle); } // 用户再猜灯谜 else if (message.startsWith("谜底是:")) { String guess = message.substring("谜底是:".length()).trim(); // 校验灯谜答案是否正确 String checkGuess = aiService.checkGuess(guess); String sendMessage = ""; if (StringUtils.hasText(checkGuess)) { // 正确发送恭喜消息 sendMessage = "恭喜你,回答正确!答案是:" + checkGuess + "。积1分!"; // 积分 scoreService.score(ChannelEnum.DM, super.getSpeakUser(event), ScoreEnum.ONE); } else { sendMessage = "很抱歉,回答错误,请继续猜测!"; } super.sendAtMessage(event, sendMessage); } } @Autowired private AiService aiService; @Autowired private ScoreService scoreService; }
3.4.2 灯谜题库(部分)
package com.lucky.star.midautumn.robot.enums; /** * 灯谜题库 * * @author bamboo panda * @version 1.0 * @date 2023/9/15 10:33 */ public enum RiddleEnum { RIDDLE_1("一年四季,不用铲除,却经常蹲着,等你来拿。", "季节"), RIDDLE_2("白又白,衣裳薄,走路摇晃像风吹。", "纸"), RIDDLE_3("身穿红袍,头戴红冠,不是人间出,却坐银山。", "太阳"), RIDDLE_4("一只铁桶,盖在楼上,盖在楼下,装满了窝头。", "井"), RIDDLE_5("绿叶红花,不点不发,谁点谁傻,谁不点谁聪明。", "电灯"), RIDDLE_6("一只小船,飘荡在海上,风雨无阻,人人称赞。", "信"), RIDDLE_7("两个小孩,搬起石头来,金子银子,尽往下放。", "秤"), RIDDLE_8("绿头红脑,站着不动,吃了一顿,跑得很快。", "蚂蚁"), RIDDLE_9("两只黄鹂鸣翠柳,一条小河流波浪,一个小姑娘在上面,洗衣服嗷嗷叫。", "笼子"), RIDDLE_10("红花绿叶,浩浩荡荡,一边走一边唱,人人都夸奖。", "火车"), // ... ; private final String riddle; private final String answer; RiddleEnum(String riddle, String answer) { this.riddle = riddle; this.answer = answer; } public String getRiddle() { return riddle; } public String getAnswer() { return answer; } }
3.4.3 机器人启动服务
package com.lucky.star.midautumn.robot.service.discord; import com.lucky.star.midautumn.robot.constant.DiscordEnv; import com.lucky.star.midautumn.robot.listener.UserMessageCreateListener; import com.lucky.star.midautumn.robot.util.SpringContextHolder; import org.javacord.api.DiscordApi; import org.javacord.api.DiscordApiBuilder; import org.javacord.api.entity.channel.ServerChannel; import org.javacord.api.entity.channel.TextChannel; import org.javacord.api.entity.message.embed.EmbedBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.awt.*; import java.util.Collection; import java.util.Iterator; /** * discord bot 启动 * * @author bamboo panda * @version 1.0 * @date 2023/9/15 10:39 */ @Component("discordApiService") public class DiscordApiService { protected final Logger logger = LoggerFactory.getLogger(DiscordApiService.class); private DiscordApi currentApi; public void init() { String apiToken = DiscordEnv.DISCORD_API_TOKEN; try { logger.info("中秋节机器人开始启动,使用token:{}", apiToken); currentApi = new DiscordApiBuilder().setToken(apiToken).login().join(); Collection channels = currentApi.getServerChannels(); for (Iterator iterator = channels.iterator(); iterator.hasNext(); ) { ServerChannel serverChannel = (ServerChannel) iterator.next(); logger.info("中秋节机器人监听频道: ID:{}; NAME:{}", serverChannel.getIdAsString(), serverChannel.getName()); } logger.info("************************************"); logger.info("**********中秋节机器人启动完成**********"); logger.info("************************************"); } catch (Exception e1) { logger.error("中秋节机器人启动异常 err:" + e1.getMessage(), e1); } // 添加机器人消息监听事件 currentApi.addMessageCreateListener((UserMessageCreateListener) SpringContextHolder.getBean("userMessageCreateListener")); // 发送活动消息 this.sendDiscordUserMassage("中秋活动诗歌现场规则r" + "大家随意发挥,只要是包含月亮,中秋相关的诗歌就可以获得1积分", DiscordEnv.SG_CHANNEL); // 发送活动消息 this.sendDiscordUserMassage("中秋活动云月饼现场规则r" + "大家随意发挥,只要成功制作了一个月饼,将可以获得6积分", DiscordEnv.YB_CHANNEL); } /** * 发送机器人消息 * * @param message 消息内容 * @param channelId 消息频道 * @return */ public boolean sendDiscordUserMassage(String message, String channelId) { try { TextChannel channel = currentApi.getChannelById(channelId).get().asTextChannel().get(); EmbedBuilder emb = new EmbedBuilder(); emb.setColor(new Color(233, 38, 99)); emb.setDescription(message); channel.sendMessage(emb); return true; } catch (Exception e) { logger.error(e.getMessage(), e); } return false; } }
3.4.4 Discord Bot 相关依赖
org.javacord javacord 3.3.2 pom javax.servlet javax.servlet-api org.bouncycastle bcprov-jdk15on
四、活动效果图
4.1 了解中秋节历史文化
4.2 月饼和中秋节的故事
4.3 中秋传统游戏猜灯谜
4.4 中秋节诗歌PK赛点
4.5 制作第一个云月饼
4.5 中秋节限定积分模式
五、活动预热、宣传等安排
宣传时间预热
北京时间:2023-09-16~2023-09-27
宣传方式
Discord 宣传
-
创建专用频道:在 Discord 服务器中创建一个专门用于活动宣传的频道,确保它易于找到并与其他频道区分开来。
-
定期发布宣传内容:按照事先制定的发布频率,在 Discord 频道中发布宣传文案、图像或视频,以吸引参与者的关注。
-
互动和回答问题:及时回答用户在宣传频道中提出的问题,提供活动的详细信息,并鼓励他们参与讨论。
Twitter 宣传:
-
创建宣传主题标签:为活动创建一个独特的主题标签(hashtag),在推文中使用该标签来集中关注和引起讨论。
-
撰写引人注目的推文:在推文中使用精炼的文案,吸引用户点击和参与,同时附带活动相关的图片或视频素材。
-
与用户互动:回复用户的评论和提问,与他们建立互动,激发更多的兴趣和参与度。
活动时间
北京时间:2023-09-28~2023-09-30(限期三天)
最后:==预祝活动成功==