某机器人开发框架和机器人服务的项目总仓库.
QQ/TG 机器人开发在这入门
本项目的文档 »
加入讨论
·
报告问题
·
提供建议
ProjHyperai 具有以下子项目
- Hyperai - Hyperai 项目就是 Hyperai 机器人开发框架所依赖的基础设施, 包含事件定义到消息实现, 以及用于构造 Hyperai Application 的定义及默认实现.
- Hyperai.Units - 可集成到 Hyperai Application 的模块, 提供类似 MVC 中 Controller 的开发体验.
- HyperaiShell - 个人自己实现的 Hyperai Application , 集成了大部分模块和服务, 开箱即用, 用插件扩展. 除非你觉得能做的更好, 不然就用它吧.
- 我想自己构造机器人程序或集成 Hyperai 到已有的程序中 => Hyperai
- 除了上一条, 我还想有 Units 模块 => Hyperai + Hyperai.Units
- 我不想写代码, 只想白嫖 => HyperaiShell + 来自社区的插件
- 不想写那么多胶水代码, 只想专注于机器人的特定功能 => HyperaiShell + 自己写的插件
- 我全都要 => HyperaiShell + 来自社区的插件 + 自己写的插件
指 HyperaiShell 插件的开发体验, 反正前几个都没人用…
public override void OnMemberJoin(object sender, GroupMemberJoinEventArgs args)
{
var chain = $"[hyper.at({args.Who.Identity})]TA来了!".MakeMessageChain(); // 特殊码是 Hyperai 内定义的, 不管在哪都是同一个实现, 放心写. 为什么要加个前缀 hyper? 为了区分其他第三方实现, 同时便于肉眼/编辑器搜索识别.
args.Group.SendAsync(chain); // 等不等待都无所谓
}
public override void OnMemberLeave(object sender, GroupMemberLeaveEventArgs args)
{
if(args.IsKicked)
{
var chain = MessageChain.Construct(new Plain($"{args.Who.DisplayName}({args.Who.Identity})滚蛋了!"));
args.Group.SendAsync(chain).Wait(); // 那就等吧
}else
{
var builder = new MessageChainBuilder();
builder.AddPlain($"{args.Who.DisplayName}({args.Who.Identity})");
builder.AddPlain("TA走了...");
var messageId = args.Group.SendAsync(builder.Build()).GetAwaiter().GetResult(); // 发送消息会返回 MessageId, 为了获取它还是等一下吧.
}
}
// MessageChain 本质是 IEnumerable<MessageComponent>,
// 能用所有的 Linq 语句.
// 这就够了.
// 不够? 需要一个文本表示?
var hypertext = MessageChain.Construct(new At(10000), new Plain("我俏丽吗!")).Flatten();
// -> hypertext = "[hyper.at(10000)]我俏丽吗!"
// 这是 string.MakeMessageChain 的逆过程, 内部用的是 IMessageChain(Formatter/Parser)
// 你永远可以相信这种操作输出是具有可依赖性的
// "我能不能用 MessageChain.ToString 来文本化?"
// 可以, 但是结果是不确定的, 不建议把输出拿来做文本处理
// MessageChain.ToString 内部调用了 MessageComponent.ToString
// 而 ToString 是用来展示对象结构的, 不是给程序"读"的.
// 你可以 Console.WriteLine(messageChain.ToString()) 但不可以 messageChain.ToString().IndexOf("at")
// 因为后者不一定会把 @某人 转换为文本"at", 甚至可能转换为"@了某个人"
private async Task DelayedTask(Friend friend)
{
var chain = "我俏丽吗?(y/n)".MakeMessageChain();
await friend.SendAsync(chain);
friend.Await(FuckReply);
void FuckReply(MessageContext context)
{
if(context.Message.Flatten().ToLower() == "y")
{
await context.ReplyAsync("你说说我哪里俏丽.".MakeMessageChain());
// 在该语境下可以确定 context.User 一定是 Friend
((Friend)context.User).Await(context => context.ReplyAsync("这可是你说的.".MakeMessageChain()).Wait());
}else
{
await context.ReplyAsync("???".MakeMessageChain());
}
}
}
void Foo(IApiClient client)
{
client.On<FriendMessageEventArgs>(new DefaultEventHandler(args => args.User.SendPlainAsync("[自动回复]正忙, 稍后回复.").Wait());
}
void Bar(IHyperaiApplicationBuilder app)
{
app.Use<FooBarMiddleware>();
}
// 是的, 中间件是具名的, 没有匿名中间件. 既然要实现中间件构造注入, 那就让它具名吧.
class FooBarMiddleware: IMiddleware
{
private readonly ILogger _logger;
public FooBarMiddleware(ILogger<FooBarMiddleware> logger)
{
_logger = logger;
}
public bool Run(GenericEventArgs args)
{
_logger.LogInformation("I got it!");
}
}
void Fuck(IBotCollectionBuilder builder)
{
// 你需要先注册这个 bot 它才会起效.
builder.Add<FuckingBot>();
}
class FuckingBot: BotBase
{
public override void OnFriendMessage(object sender, FriendMessageEventArgs args)
{
args.User.SendPlainAsync("sodayo").Wait();
}
}
void Suck()
{
// 因为 Unit 是自动寻找的, 所以不需要手动添加
}
class UnitSucks: UnitBase
{
[Receive(MessageEventType.Group)]
// [Extract("*[hyper.at(@Self.Identity)]*")] // 中间的 @Self.Idenetity 并没有实现...
[Extract("*[hyper.at({who})]*")] // 但是你可以这么写, 然后判断一下
public async Task Milky(long who, Group where, Self me)
{
if(who == me.Identity)
{
await where.SendPlainAsync("叫👴干嘛?");
}
}
}
配置是只读的, 就别想着怎么写了; 就算写也只是 in-memory, 不会保存到文件的
// 只能依赖注入
// "哪些地方能依赖注入?" 看文档https://projhyperai.dowob.vip, 里面有写
class Rock
{
private readonly IConfiguration _configuration;
public Rock(IConfiguration configuration)
{
_configuration = configuration;
}
private void Peek()
{
if(_configuration["Application:SelectedClient"].ToLower().Contains("mirai"))
{
// Aha, 你在用 mirai!
}
}
}
class APlugin: PluginBase
{
private readonly IConfiguration _configuration;
public PluginEntry()
{
// PluginBase 不支持任何注入, 也不能访问外部服务, 唯一获取服务的方式是 PluginBase.Context, 里面提供了已经配制好的服务.
_configuration = Context.Configuration;
}
}
class AnUnit: UnitBase
{
private readonly IConfiguration _configuration;
public AnUnit(IPluginConfiguration<APlugin> configuration)
{
// UnitBase 可以访问服务, BotBase 同理.
// IPluginConfiguration 需要泛型参数来指定是哪个插件的私有配置
_configuration = configuration.Value;
}
}
// 和上一部分是一样的, HyperaiShell 中的数据库由 LiteDb 提供
// 对应的类型为 IRepository 和为插件用的 IPluginRepository<TPlugin>
本仓库包含多个子项目, 并提供了一个 Visual Studio 解决方案用于同时控制子项目.
不同子项目之间依赖包而不是项目, 想要快速应用某个子项目的更改到其他项目请将该子项目打包并添加到本地的离线包源.
git clone --recursice https://github.com/theGravityLab/ProjHyperai.git
git checkout dev
使用文本编辑器打开 ./docs
目录即可开始工作. 提交 pr 即可应用修改.
git clone https://github.com/theGravityLab/ProjHyperai.git
git checkout dev