本文最后更新于 2025年6月14日 凌晨
                  
                
              
            
            
              
                
                前言
在上一篇教程中,我们学习了如何调用api发送消息,以及发送不同的消息类型,那我们今天来实战写一个签到插件
签到插件的原理就是写入今天的时间,如果判断上一次签到时间和今天是一致的,那就签到失败,否则就是签到成功
指令匹配
在Koishi这个框架中,我们除了可以用ctx.on去判断消息外,我们可以直接使用最直接的ctx.command去判断消息
示例代码如下
| 12
 3
 4
 
 | ctx.command('签到').action(async({session}) => {
 await session.send("1")
 })
 
 | 

数据库基础使用
这里是点我具体的文档,那么我们来写一个基础的,首先是声明数据类型
| 12
 3
 4
 
 | export interface sign{time : string
 user_id : string
 }
 
 | 
第二步声明使用了数据库插件,否则会出现黄色警告
| 1
 | export const inject = ["database"]
 | 
在Koishi里面声明数据库
| 12
 3
 4
 5
 
 | declare module 'koishi' {interface Tables {
 sign : sign
 }
 }
 
 | 
最后是创建数据库
| 12
 3
 4
 
 | ctx.model.extend('sign',{time: 'string',
 user_id: 'string'
 })
 
 | 
数据库创建代码为
| 1
 | await ctx.database.create(table_name, {.....})
 | 
数据库更新代码为
| 1
 | await ctx.database.upsert(table_name, [{....}])
 | 
数据库搜索代码为
| 1
 | await ctx.database.get(table_name, {....})
 | 
获取年月日
这里我们可以使用js的库获取
| 12
 3
 4
 5
 6
 7
 
 | function getCurrentDate(): string {const date = new Date();
 const year = date.getFullYear();
 const month = String(date.getMonth() + 1).padStart(2, '0');
 const day = String(date.getDate()).padStart(2, '0');
 return `${year}-${month}-${day}`;
 }
 
 | 
开始编写
在我们前面了解完这些知识后,我们先创建一个存放签到数据的数据库
根据上面的代码讲解,我们可以这样写
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 
 | import { Context, Schema, h } from 'koishi'
 export const name = 'test'
 
 export interface Config {}
 
 declare module 'koishi' {
 interface Tables {
 sign_today : sign_today
 }
 }
 
 export const inject = ["database"]
 
 export const Config: Schema<Config> = Schema.object({})
 
 export interface sign_today {
 id?: number
 now_time: string
 user_id: string
 }
 
 export function apply(ctx: Context) {
 ctx.model.extend("sign_today", {
 id: 'unsigned',
 now_time: 'string',
 user_id: 'string'
 }, {
 primary: 'id',
 autoInc: true
 })
 
 ctx.command('签到')
 .action(async({session}) => {
 await session.send("1")
 })
 }
 
 | 
我们运行一下代码,如果日志出现了
| 12
 
 | 2025-06-14 00:57:40 [I] hmr reload plugin at external\test\src\index.ts2025-06-14 00:57:40 [I] sqlite auto creating table sign_today
 
 | 
说明你的数据库已经初始化了,现在我们缺少读写数据库
读写内容
对数据库进行搜索,然后数据库会返回数组
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | ctx.command('签到').action(async({session}) => {
 var get_user_sign_time = await ctx.database.get("sign_today", {user_id: session.userId})
 if(get_user_sign_time.length == 0){
 
 }else{
 
 }
 })
 
 | 
当返回长度为0的数组,说明这个人不存在,我们要创建数据,然后返回签到成功
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | ctx.command('签到').action(async({session}) => {
 var get_user_sign_time = await ctx.database.get("sign_today", {user_id: session.userId})
 if(get_user_sign_time.length == 0){
 await ctx.database.create("sign_today", {
 "now_time": getCurrentDate(),
 "user_id": session.userId
 })
 await session.send("签到成功")
 }else{
 
 }
 })
 
 | 
那么我们就要讨论另外一种情况了,如果这个人在数据库有记录的话,我们需要对比时间,如果时间恰好等于今天的时间,说明他已经签到了,否则就是失败
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | ctx.command('签到').action(async({session}) => {
 var get_user_sign_time = await ctx.database.get("sign_today", {user_id: session.userId})
 if(get_user_sign_time.length == 0){
 await ctx.database.create("sign_today", {
 "now_time": getCurrentDate(),
 "user_id": session.userId
 })
 await session.send("签到成功")
 }else{
 if(get_user_sign_time[0]['now_time'] == getCurrentDate()){
 await session.send("签到失败,你今天已经签到了")
 }else{
 await ctx.database.upsert("sign_today", [{
 "now_time" : getCurrentDate(),
 "user_id" : session.userId,
 }])
 await session.send("签到成功")
 }
 }
 })
 
 | 
好了,这样就大功告成了

效果如上图
代码示例
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 
 | import { Context, Schema, h } from 'koishi'
 export const name = 'test'
 
 export interface Config {}
 
 declare module 'koishi' {
 interface Tables {
 sign_today : sign_today
 }
 }
 
 export const inject = ["database"]
 
 export const Config: Schema<Config> = Schema.object({})
 
 export interface sign_today {
 id?: number
 now_time: string
 user_id: string
 }
 
 export function apply(ctx: Context) {
 ctx.model.extend("sign_today", {
 id: 'unsigned',
 now_time: 'string',
 user_id: 'string'
 }, {
 primary: 'id',
 autoInc: true
 })
 
 ctx.command('签到')
 .action(async({session}) => {
 var get_user_sign_time = await ctx.database.get("sign_today", {user_id: session.userId})
 if(get_user_sign_time.length == 0){
 await ctx.database.create("sign_today", {
 "now_time": getCurrentDate(),
 "user_id": session.userId
 })
 await session.send("签到成功")
 }else{
 if(get_user_sign_time[0]['now_time'] == getCurrentDate()){
 await session.send("签到失败,你今天已经签到了")
 }else{
 await ctx.database.upsert("sign_today", [{
 "now_time" : getCurrentDate(),
 "user_id" : session.userId,
 }])
 await session.send("签到成功")
 }
 }
 })
 }
 
 function getCurrentDate(): string {
 const date = new Date();
 const year = date.getFullYear();
 const month = String(date.getMonth() + 1).padStart(2, '0');
 const day = String(date.getDate()).padStart(2, '0');
 return `${year}-${month}-${day}`;
 }
 
 | 
后记
学习了如何通过数据库读写实现签到功能后,下期我们来写一个定时任务计划