mirai-net-shelter / mirai.net Goto Github PK
View Code? Open in Web Editor NEWMirai.Net是基于mirai-api-http实现的轻量级mirai社区sdk。
License: GNU Affero General Public License v3.0
Mirai.Net是基于mirai-api-http实现的轻量级mirai社区sdk。
License: GNU Affero General Public License v3.0
发生了什么事?
当我使用ws连接mcl之后 能正常发消息,但是收不到消息
怎么复现?
我在很多台电脑上 都试过使用C#程序连接 mcl
连接成功后 可以成功发送消息,但是得反复连接尝试很多次才能收到消息
背景:
机器人同时收到多条消息时,似乎是按照先后顺序逐条响应的。如果前一个消息的没有处理完,会一直处于等待状态
我实现的功能中有些比较耗时,需要等待网络请求和下载图片,容易阻碍后面消息
问题:
不知道有没有开启异步处理的设置选项,可以提高消息处理的效率?
谢谢!
版本:
mirai-console 2.12.1
mirai-api-http 2.6.2
Mirai.Net 2.4.6
初始化:
MiraiBot bot = new MiraiBot()
{
Address = new ConnectConfig()
{
HttpAddress = "127.0.0.1:9908",
WebsocketAddress = "127.0.0.1:9908",
},
VerifyKey = "1234567890",
QQ = $"{BotId}",
};
await bot.LaunchAsync();
//监听事件
bot.MessageReceived.OfType<GroupMessageReceiver>().Subscribe(BotMessageHandler.HandleGroupMessage);
bot.MessageReceived.OfType<FriendMessageReceiver>().Subscribe(BotMessageHandler.HandleFriendMessage);
bot.MessageReceived.OfType<TempMessageReceiver>().Subscribe(BotMessageHandler.HandleTempMessage);
bot.EventReceived.OfType<NewFriendRequestedEvent>().Subscribe(BotRequestHandler.HandleNewFriendRequest);
bot.EventReceived.OfType<NewInvitationRequestedEvent>().Subscribe(BotRequestHandler.HandleNewInvitationRequest);
在Http下,该项目使用了大量的MiraiBot单例进行HTTP请求,对于多个MiraiBot对象没办法单独使用,只能使用最新的一个Bot实例。
大部分在 Mirai.Net.Sessions.Http.Managers 、Mirai.Net.Utils.Internal.MiraiHttpUtils 下
拿MiraiHttpUtils中的一个举例
该段引用了静态单例实例,MiraiBot.Instance.HttpSessionKey,从而导致无法对不同的MiraiBot对象进行操作。
所以我觉得应该改为以下格式,应该传入MiraiBot实例,改为可以单独使用的静态方法,然后添加this关键字弄成扩展方法,这样Mirai对象可以直接点出该方法进行使用
internal static async Task<string> GetAsync(this MiraiBot bot, string url, bool withSessionKey = true)
{
var result = withSessionKey
? await url
.WithHeader("Authorization", $"session {bot.HttpSessionKey}")
.GetAsync()
: await url.GetAsync();
var response = await result.GetStringAsync();
bot.EnsureSuccess(response, $"url={url}");
return response;
}
这只是一个建议,我是无所谓的,只是觉得不合理。静态拓展方法不应该使用单例Bot,应该使用参数进行传参,否则就没必要封装一个静态拓展方法。
调用的代码是
await GroupManager.MuteAsync((string)target, e.Sender.Group.Id, (TimeSpan)ts);
异常的message为
原因: 上传文件错误
备注: url=localhost:端口/memberInfo?target=group&memberId=“需要禁言的qqID”
不知道是我调用错误还是确实有问题(
复现步骤:
我看了这个 WebSocket 库默认启用自动重连的,但是不知道为什么没起作用。(是否需要额外的设置?)
我目前的解决方案是处理 WebSocketClient 的 DisconnectionHappened 事件,在断线后调用 LaunchAsync 重新建立连接。
Data
entity classesMessageChain
如题,目前例如获取群员列表等操作全都放在了AccountManager
中,在实际使用过程中感觉很反直觉(这部分也没有在文档中列出,我还是翻源码才发现它是在AccountManager
里的…)
希望将AccountManager
的.GetGroupMembersAsync()
获取群成员列表,.GetGroupsAsync()
获取群列表等方法在GroupManager中建立映射。
其实我是觉得应该直接把这些方法放在GroupManager
里会更好一些的,尤其是目前获取群成员Member
对象的方法位于GroupManager.GetMemberAsync()
,而获取群成员Profile
的方法位于AccountManager.GetMemberProfileAsync()
,这部分感觉有些混乱了。
发生了什么事?
使用FileManager.UploadFileAsync()可以正常上传英文数字文件名的文件,但是当文件名包含中文日文等字符时MAH就会反馈500错误,上传失败。提示信息类似于
2023-03-14 08:57:21 E/MAH Access: java.lang.IllegalArgumentException: Chars ':*?"<>|' are not allowed in path. RemoteFile path contains illegal char: '?'. path='=?utf-8?B?5rWLMTg4NDY1OTguemlw?='
java.lang.IllegalArgumentException: Chars ':*?"<>|' are not allowed in path. RemoteFile path contains illegal char: '?'. path='=?utf-8?B?5rWLMTg4NDY1OTguemlw?='
at net.mamoe.mirai.internal.utils.FileSystem.checkLegitimacy(FileSystem.kt:17)
at net.mamoe.mirai.internal.utils.FileSystem.normalize(FileSystem.kt:26)
at net.mamoe.mirai.internal.contact.file.RemoteFilesImpl$Companion.findFileByPath(RemoteFilesImpl.kt:34)
at net.mamoe.mirai.internal.contact.file.CommonAbsoluteFolderImpl.uploadNewFile$suspendImpl(AbsoluteFolderImpl.kt:400)
at net.mamoe.mirai.internal.contact.file.CommonAbsoluteFolderImpl.uploadNewFile(AbsoluteFolderImpl.kt)
at net.mamoe.mirai.contact.file.AbsoluteFolder.uploadNewFile$default(AbsoluteFolder.kt:194)
at mirai-api-http-2.9.1.mirai2.jar//net.mamoe.mirai.api.http.adapter.internal.action.FileKt.onUploadFile(file.kt:62)
at mirai-api-http-2.9.1.mirai2.jar//net.mamoe.mirai.api.http.adapter.http.router.FileKt$fileRouter$1$invoke$$inlined$httpAuthedMultiPart$1$1.invokeSuspend(dsl.kt:228)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
怎么复现?
直接使用FileManager.UploadFileAsync()上传文件名包含中文的文件就可以复现,除了http返回外,Mirai窗口也会有错误提示。
环境
补充信息
在MAH处提出了issue #698
似乎这是.NET Standard 2.0的MultipartFormDataContent
方法本身有问题,而使用了它的FlUrl等也自然有了同样的问题。
.net 6.0的MultipartFormDataContent将file=项进行了错误的编码,而MAH并不支持这样的编码。
例如当编码新建文本文档.txt
时,MultipartFormDataContent会将filename项编码为filename="=?utf-8?B?5paw5bu65paH5pys5paH5qGjLnR4dA==?="
。根据上面issue中其他人的解答,这部分编码的方式是错误的。MAH接收到这串字符串后,会直接将其作为文件名,导致出错。正常情况下,应该直接发送UTF-8编码的filename="新建文本文档.txt"
此问题目前暂且只能通过手搓轮子来解决。希望框架能原生支持。
var hackedFileName = new string(Encoding.UTF8.GetBytes(fileName).Select(b => (char)b).ToArray());
streamContent.Headers.Add("Content-Disposition", $@"form-data; name=file; filename=""{hackedFileName}""; filename*=""{hackedFileName}""");
content.Add(streamContent);
通过以上方式而非FlUrl的.AddFile()方法添加文件
我目前搓的轮子完整代码如下,供参考
public static async Task<Mirai.Net.Data.Shared.File> UploadFile(
string groupId,
string filePath,
string uploadPath = "", string fileName = null)
{
string url = string.Format("http://{0}/file/upload", MiraiBot.Instance.Address.HttpAddress);
fileName ??= Path.GetFileName(filePath);
using var httpClient = new HttpClient();
using var content = new MultipartFormDataContent
{
{ new StringContent(MiraiBot.Instance.HttpSessionKey), "sessionKey" },
{ new StringContent("group"), "type" },
{ new StringContent(groupId), "target" },
{ new StringContent(uploadPath), "path" }
};
// 从文件加载数据
var streamContent = new StreamContent(File.Open(filePath, FileMode.Open));
var hackedFileName = new string(Encoding.UTF8.GetBytes(fileName).Select(b => (char)b).ToArray());
streamContent.Headers.Add("Content-Disposition", $@"form-data; name=file; filename=""{hackedFileName}""; filename*=""{hackedFileName}""");
content.Add(streamContent);
// 发送POST请求
var responseBody = await (await httpClient.PostAsync(url, content)).Content.ReadAsStringAsync();
//Console.WriteLine(responseBody);
JObject re = responseBody.ToJObject();
Mirai.Net.Data.Shared.File file = !re.ContainsKey("name") ? null : re.ToObject<Mirai.Net.Data.Shared.File>();
return file;
}
{
"code":0,
"msg":"",
"data": [
{
"name":"setu.png",
"id":"/12314d-1wf13-a98ffa",
"path":"/setu.png",
"parent":null,
"contact":{
"id":123123,
"name":"setu qun",
"permission":"OWNER"
},
"isFile":true,
"isDictionary":false,
"isDirectory":false,
"downloadInfo":{
"sha1":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"md5":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"downloadTimes":10,
"uploaderId":123456789,
"uploadTime":1631153749,
"lastModifyTime":1631153749,
"url":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
}
}
]
}
public class FileDownloadInfo
{
[JsonProperty("sha1")] public string Sha1 { get; set; }
[JsonProperty("md5")] public string Md5 { get; set; }
[JsonProperty("url")] public string Url { get; set; }
}
目前框架只有VerifyKey
是public类型,而HttpSessionKey
是internal并且没有提供外部读取方法。
同时,PostJsonAsync()
等较为底层的方法也没有公开,导致使用者无法直接自行向MAH发送非标准的请求。
例如MAH的插件Mirai Hibernate Http,此插件提供了多个新的框架自身未支持的Route,例如GET /message/group?bot={}&group={}&start={}&end={}
获取指定群的群聊记录。
或者比如 #72 中遇到的情况,需要自己构建特殊的MAH请求时。
以上情况都需要当前bot实例的SessionKey才可以进行。所以希望框架能够提供访问当前SessionKey的方法。
(或者是直接开放允许用户自定义Route与请求的PostJsonAsync()
方法也可以实现上面提到的需求场景)
多谢!
发生了什么事?
无法获取NudgeEvent
怎么复现?
bot.MessageReceived
.OfType<NudgeEvent>()
.Subscribe(Received =>
{
Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}][INFO]收到来自{Received.FromId}的戳一戳。");
if (Received.Target != "114514" || Received.FromId == "114514")
{
return;
}
else
{
MessageManager.SendNudgeAsync(Received.FromId, Received.Subject.Id, MessageReceivers.Group);
}
});
你以为它会做什么?
本应在收到戳一戳消息后在控制台输出消息来源,并且戳发出戳一戳的人,但似乎并无法收到NudgeEvent消息
Mirai控制台可以看见Mirai是有收到戳一戳的。
环境
希望支持Mirai中的消息序列化方法,serializeToMiraiCode()
目前Mirai.Net只有GetPlainMessage()。没有办法直接对比2条消息内容是否相同。
由于消息链中包含有消息的源数据,因此即使是2条复读的消息,其中的Source部分也有差异。当使用==直接对比两条消息链时总是会得到False的结果。
除了消息链外,Image等单个元素也会有类似的问题,即使是同一张图片连续发送,ImageID相同,但是因为每一次的图片地址url
都有变化,所以导致即使是同一个消息里的3张ID相同的表情包,判断是否相等时候结果也是False。
目前我能想到的实现方法是逐个元素对比两个消息链并直接跳过Source类型,但是对于图片等类型的元素,依然需要每个元素单独判断两者是否相等(通过imageId),非常不美观。
类似于长这样
private static bool Equals(MessageChain a,MessageChain b)
{
if (a.Count!=b.Count) return false;
for (int i = 0; i < a.Count; i++)
{
if (a[i].Type==Messages.Source || b[i].Type == Messages.Source)
continue;
if (a[i].Type == Messages.Image && b[i].Type == Messages.Image && ((ImageMessage)a[i]).ImageId == ((ImageMessage)b[i]).ImageId) continue;
if (a[i] != b[i]) return false;
}
return true;
}
如果对比两条消息的Mirai码或者酷Q码是可以简单地判断两条消息的内容是否相同的。并且一整条字符串也方便对消息本身进行整体处理(比如说,我想把消息的简体文字替换成繁体)的同时不影响消息中的表情图片、at等元素。希望能实现此特性。
非常感谢!
[INFO] Verifying "net.mamoe:mirai-console" version 2.6.6
I/main: mirai-console started successfully.
GetPluginVersion() => "2.0.2"
怎么复现?
bot.MessageReceived
.OfType<GroupMessageReceiver>()
.Subscribe(async r =>
{
if (r.MessageChain.GetPlainMessage() == "/send")
{
var localPath = @"C:\Users\ahpx\Desktop\RandomChoiceGenerator.exe";
var file = await FileManager.UploadFileAsync(r.GroupId, localPath);
await r.SendMessageAsync($"The file has been uploaded. \r\n{file.ToJsonString()}");
}
});
Existing issue: project-mirai/mirai-api-http#553
我没有其他的机器人项目做比对(我看过别群的机器人速度为1s左右),为什么发送消息的时间需要2-3s,这是正常现象还是框架所限还是我自己程序上的问题呢?
Mirai里的http api文档里有anno/list(获取群公告) 和anno/publish(发布群公告)
但是在框架里没有找到这两个功能,目前在框架中只看到一个入群公告改变事件。
能否增加这两个新功能?
万谢感谢!
发生了什么事?
升级2.4.4之后无法订阅到事件,回退2.4.3后正常
怎么复现?
bot.MessageReceived
.OfType()
.Subscribe(async r =>
{
if (r.Sender.Id != bot.QQ)
{
var msg = r.MessageChain.GetPlainMessage().Trim();
var repMsg = await robotService.GeneralMessageProcess(msg, r.Sender.Id);
await r.SendMessageAsync(repMsg);
_logger.LogInformation($"回复好友消息:{repMsg}");
}
});
环境
如果你觉得Mirai.NET
现阶段做得还不够好,请回复此issue来说明你的需求是怎么样的,以及你的预期是什么。
{
"type": "BotLeaveEventDisband",
"group": {
"id": 123456789,
"name": "Miral Technology",
"permission": "MEMBER"
},
operator: null
}
Location Mirai.Net.Utils.Scaffolds.MiraiScaffold.QuoteGroupMessageAsync()
public static async Task<string> QuoteGroupMessageAsync(this GroupMessageReceiver receiver, string message)
{
var id = receiver.MessageChain.ToList().OfType<SourceMessage>().First().MessageId;
return await MessageManager
.QuoteGroupMessageAsync(receiver.Sender.Group.Id, id, message);
}
需要构建合并转发消息的工具
目前手动构建相当麻烦
感谢作者.jpg
在mirai控制台上能够看到图片类型的消息带有IsEmoji=true/false的输出,这个可以识别电脑端消息中表情显示的大小,如果IsEmoji是false那么图片会尽可能的根据原尺寸显示,否则就是一个较小的尺寸显示。希望Mirai.Net中也可以支持获取这个字段
谢谢!
HTTPS及WSS
发生了什么事?
AccountManager.GetMemberProfileAsync无法获取到nickname
怎么复现?
nickname = AccountManager.GetMemberProfileAsync(ms.num.ToString(), gs.num.ToString()).Result.NickName;
你以为它会做什么?
我认为他会得到nickname,但实际上nickname的值是""(为空)
我试图把他改成
Profile pf = await AccountManager.GetMemberProfileAsync(ms.num.ToString(), gs.num.ToString());
但是IDE告诉我await只能用于异步方法
请原谅我较低的编程水平
如果有错误请指出,不甚感激
环境
I'm always frustrated when I need to get members' nickname since i have to use the account manager.
Please add this property in the member class.
Thank you for your great work!
发生了什么事?
目前EnsureSuccess()
方法中的json.OfErrorMessage()
部分在code=500时会直接返回文件上传错误。并且抛出的错误中,只包含引发错误时Mirai.Net发送的数据,而没有包括MAH端的回应。因此无法根据回应来判断错误的具体内容
举例来说,比如机器人短时间内群发太多内容触发了风控的话,会得到以下回应(其中***部分为群号)
{
"code": 500,
"msg": "Failed sending message to Group(**********), reason=LIMITED_MESSAGING. Tips: 问题原因可能是账号被多次举报或被服务器认为不安全. 若账号在官方客户端也无法发出消息, 可尝试用手机 QQ 登录后访问 https://accounts.qq.com/safe/message/unlock?lock_info=5_5 解冻."
}
此外,也包含以下等各种其他情况
500
Send message failed: MessageSvcPbSendMsg.Response.Failed(resultType=110, errorCode=0, errorMessage=发送失败,你已被移出该群,请重新加群。)
500
message is empty
由于在发送消息的过程中,EnsureSuccess()
抛出的异常并不包括返回的msg
部分,导致无法在程序中判断错误的具体原因并进行处理。
例如一个应用场景:如果机器人发送消息时触发了上面的风控导致消息发不出去,那么就通过私聊告诉机器人的主人提醒手动上号解冻。
那么这里就需要自己在机器人中手动构筑一个自己的SendGroupMessage()
方法封装MessageManager.SendGroupMessageAsync()
来捕获异常,并根据异常的种类来判断是否需要发送私聊消息进行上报。
怎么复现?
如上面的例子,可以简单地直接发送空消息至群聊,或发送至已经被解散的群、已经退出的群号等,都可以得到MAH的500错误反馈。
修改建议
希望throw出包含更多内容的自定义异常类,而非直接将错误文字拼装成string并返回。
例如将code与msg封装入exception的自定义字段中,使得程序在catch异常ex后,可以通过ex.code\ex.msg\ex.payload等属性分别获取错误的各项具体内容
环境
如题
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.