-
Notifications
You must be signed in to change notification settings - Fork 494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for new Steam chat #561
Comments
Check out the |
Trying to start with I guess I'll just wait for finished implementation instead, unless you have extra surplus of time and willings to check below and find out what is wrong 😓 internal sealed class ArchiBetaHandler : ClientMsgHandler {
public override void HandleMsg(IPacketMsg packetMsg) { }
internal async Task<bool> SendChatMessage(ulong chatGroupID, ulong chatID, string message) {
CChatRoom_SendChatMessage_Request request = new CChatRoom_SendChatMessage_Request {
chat_group_id = chatGroupID,
chat_id = chatID,
message = message
};
try {
await SendMessage("ChatRoom.SendChatMessage#1", request);
} catch {
return false;
}
return true;
}
private AsyncJob<SteamUnifiedMessages.ServiceMethodResponse> SendMessage<TRequest>(string name, TRequest message, bool isNotification = false) where TRequest : IExtensible {
if (message == null) {
throw new ArgumentNullException(nameof(message));
}
ClientMsgProtobuf<CMsgClientServiceMethod> msg = new ClientMsgProtobuf<CMsgClientServiceMethod>(EMsg.ServiceMethodCallFromClient) { SourceJobID = Client.GetNextJobID() };
using (MemoryStream ms = new MemoryStream()) {
Serializer.Serialize(ms, message);
msg.Body.serialized_method = ms.ToArray();
}
msg.Body.method_name = name;
msg.Body.is_notification = isNotification;
Client.Send(msg);
return new AsyncJob<SteamUnifiedMessages.ServiceMethodResponse>(Client, msg.SourceJobID);
}
}
[Serializable]
[ProtoContract(Name = nameof(CChatRoom_SendChatMessage_Request))]
public class CChatRoom_SendChatMessage_Request : IExtensible {
[ProtoMember(1, IsRequired = false, Name = "chat_group_id", DataFormat = DataFormat.FixedSize)]
public ulong chat_group_id { get; set; }
[ProtoMember(2, IsRequired = false, Name = "chat_id", DataFormat = DataFormat.FixedSize)]
public ulong chat_id { get; set; }
[ProtoMember(3, IsRequired = false, Name = "message", DataFormat = DataFormat.Default)]
public string message { get; set; }
private IExtension extensionObject;
IExtension IExtensible.GetExtensionObject(bool createIfMissing) => Extensible.GetExtensionObject(ref extensionObject, createIfMissing);
} |
You'd probably want to take a look at the I would posit that you may not need to use the https://github.com/SteamRE/SteamKit/blob/master/Samples/8.UnifiedMessages/Program.cs for usage. |
IIRC, the body of the message is the recreated proto request. You would need to send a |
Thank you both! I managed to get first working example, sharing it for reference: internal sealed class ArchiBetaHandler : ClientMsgHandler {
public override void HandleMsg(IPacketMsg packetMsg) { }
internal async Task<SteamUnifiedMessages.ServiceMethodResponse> SendChatMessage(ulong chatGroupID, ulong chatID, string message) {
ClientMsgProtobuf<CChatRoom_SendChatMessage_Request> request = new ClientMsgProtobuf<CChatRoom_SendChatMessage_Request>(EMsg.ServiceMethodCallFromClient) {
Body = {
chat_group_id = chatGroupID,
chat_id = chatID,
message = message
},
SourceJobID = Client.GetNextJobID()
};
request.Header.Proto.target_job_name = "ChatRoom.SendChatMessage#1";
Client.Send(request);
try {
return await new AsyncJob<SteamUnifiedMessages.ServiceMethodResponse>(Client, request.SourceJobID);
} catch {
return null;
}
}
}
public class CChatRoom_SendChatMessage_Request : IExtensible {
[ProtoMember(1, IsRequired = false, Name = "chat_group_id", DataFormat = DataFormat.Default)]
public ulong chat_group_id { get; set; }
[ProtoMember(2, IsRequired = false, Name = "chat_id", DataFormat = DataFormat.Default)]
public ulong chat_id { get; set; }
[ProtoMember(3, IsRequired = false, Name = "message", DataFormat = DataFormat.Default)]
public string message { get; set; }
private IExtension extensionObject;
IExtension IExtensible.GetExtensionObject(bool createIfMissing) => Extensible.GetExtensionObject(ref extensionObject, createIfMissing);
} Now I'll try to add all missing stuff, since right now even response doesn't return, but hey, it works 🎉 |
@voided you were right too, this also works: var uniMessages = SteamClient.GetHandler<SteamUnifiedMessages>();
var request = new CChatRoom_SendChatMessage_Request {
chat_group_id = 0, // must be valid
chat_id = 0, // must be valid
message = "Test2"
};
uniMessages.SendMessage("ChatRoom.SendChatMessage#1", request) Then it's just a matter of reverse-engineering protobufs and hooking it to unified messages, perfect 🎉. |
Note to self and other people to not waste productivity over stupid things: ClientMsgProtobuf<CMsgClientUIMode> request = new ClientMsgProtobuf<CMsgClientUIMode>(EMsg.ClientCurrentUIMode) { Body = { chat_mode = 2 } };
Client.Send(request); Send this to enable beta chat mode. Otherwise you won't receive majority of callbacks and won't be able to move forward. ... Don't ask how many hours I wasted debugging only to find out about this 😅 |
I've successfully written very basic protobufs for sending and receiving new Steam group messages, this is a good start - steamclient-beta...JustArchi:archi-wip I'm not sending PR with this as we'll probably want to automate generation of those (I guess?), but feel free to make use of them for time being. I'll probably go with private messaging next. I'm wondering whether we want to make a bit more user-friendly methods to access those, and how exactly they should look like. I mean, I can totally see private async void OnServiceMethod(SteamUnifiedMessages.ServiceMethodNotification callback) {
switch (callback.MethodName) {
case "ChatRoomClient.NotifyIncomingChatMessage#1":
CChatRoom_IncomingChatMessage_Notification body = (CChatRoom_IncomingChatMessage_Notification) callback.Body;
if (body.message == "ping") {
CChatRoom_SendChatMessage_Request request = new CChatRoom_SendChatMessage_Request {
chat_group_id = body.chat_group_id,
chat_id = body.chat_id,
message = "pong"
};
await ChatRoomService.SendMessage(x => x.SendChatMessage(request));
}
break;
}
} On the other hand it might make sense to make In any case, it starts looking really good, thanks again for initial help, I'll shut up now and let you work in peace 😀. Those are just random ideas, it's your decision whether they make sense or not 🎉. |
Oh please don't, you've been very useful. 😉 Currently, between @DoctorMcKay and @xPaw, we've got a rough .proto file out of the Javascript frontend: https://github.com/SteamDatabase/Protobufs/blob/ff8a4dbb6a1ad54e8248bf87617f4686244a6d85/steam/WebUI/friends.proto I think the next step would be to clean that up so that we don't have to manually maintain all the new unified services:
|
@yaakov-h Thanks a lot! I used your I can say that this works really good, it's still very dirty but I managed to add support for everything in both of my ArchiBoT and ASF projects. Nothing really huge, but for now everything works great and I'll keep adding (and testing) other stuff. I'm positively shocked how consistent and reliable all of that is. I mean look, I can finally hook every request to its own response, strong-type all of that and finally have async output without having to deal with crap like #491 and wondering why suddenly working things broke. Thanks once again for everything, I can continue breaking things on my own now 😀. |
Compileable proto in 6593190. |
@yaakov-h I've noticed that some of new interfaces have
I've verified that this one takes |
@xPaw any ideas? ^^^ |
Well, that's how @Ne3tCode decided to do it, if there's a response proto. Looks like he managed to fix AckChatMessage and NotifyUserVoiceStatus but not others. For example, |
I checked js code [not much] carefully and can confirm that P.S. // Nephrite |
Since the issue died a bit, let me refresh it with what I managed to do in my projects to hopefully help @yaakov-h and the rest of the team to eventually tackle down this one. I didn't have much needs so I basically reverse-engineered only the parts responsible for receiving the messages and sending them (including joining chat rooms). This is enough for basic chat implementation, but there is a room for improvement in regards to stuff like e.g. parsing the messages and alike.
There are new APIs for friends management, I've added You can get ID of the chat room from the clan's ID using Finally there is For handling incoming chat messages, I hooked into All of the info above gives a sneak peek into how new chat works, but there are still SK2 project decisions that need to be reviewed before deciding to implement all of that.
Those are as usual just my thoughts, I'm not sure if they're even helpful at all and that I'm not actually confusing everybody around, but I think that instead of hunting new APIs, messages and protos, it'd be more wise to have a good foundation of the basic concepts and documentation of how those new things work together so the users could just implement what they need instead of learning that As usual feel free to browse my ASF project for working implementation of everything mentioned above. I tried my best to make it as good as it made sense in regards to my use cases, which is exactly why I realized that there is not really that much we need extra from SK2 to make it super friendly and easy to consume, merely cutting down on the excessive parsing noise and making some interfaces easier to use. |
I was wondering if this could allow the ability to join voice channels, and do things such as play music. (Like a Discord Music bot) |
@JustArchi It appears that you're basically the only user of the new chat system, would you be able to bring some of the new handlers and methods from your implementation into SK? |
If I'm the only user then SK2 doesn't need that code just for me 😁. I'm short on time, but I'll see if I can come up with some PR in regards to this, if the rest of the team would prefer that instead of coding themselves. |
I, for one, would definitely appreciate:
|
matterbridge no longer has Steam chat support due to this; see 42wim/matterbridge@9592cff and Philipp15b/go-steam#94. (I came across this because SuperTux is going to be coming to Steam, and people on the SuperTux Discord might want to bridge their Discord server with Steam chat via matterbridge) |
The go-steam team are in the same position as us, nobody has the spare time to figure out the pieces, how they fit together, and a neat API to wrap it all. Archi has done quite a bit of work above, assuming that the implementation hasn't changed and those comments are still true then it shouldn't be too hard to build wrappers in either library. |
I have a basic implementation in ASF that allows to read chat messages and write them, but I didn't have time and motivation to extract all those parts and put in SK2 as of today. Steam chat is a giant beast and requires more work than I put into it however, as proper implementation would also need to handle Steam-specific bbcode, send stickers/emotes/images, parse them and do whole lot more than what I do with my basic read/write pure plaintext. You're more than welcome to use my work if you plan on adding those bits to SK2 or any other Steam-related lib, but I'm just saying it's nowhere close to being finished and this is one of the reasons why I wasn't that eager to just put it in SK2 - because I can't commit myself to it as of now. |
FWIW the old chat implementation in steamkit still works. |
Only for private chat, group chat requires completely new implementation that ASF has. |
@cooljeanius you might be interested in icewind1991/mx-puppet-steam. |
making it a link for cases where it might not have auto-linkified: https://github.com/icewind1991/mx-puppet-steam |
I think this is being used by mx-puppet-steam: https://github.com/DoctorMcKay/node-steam-user/wiki/SteamChatRoomClient. |
Hm, maybe @DoctorMcKay can help? |
Today Steam got new chat update that is trying to mimic Discord in majority of aspects. I'm wondering if we could hope for at least basic at first, SK2 support for it.
I took a quick look and the good news are that we can use good old Steam protocol for that, which makes it fall into SK2 scope, but the bad news are that it might require some extra work:
Currently NHA2 is missing relevant bits, it looks like #477 proposed a fix, we might need something like that to fully support it.
Do you have a plan to add proper support for it in near future? I even wanted to start working on it right away but since SK2 doesn't even have a specification for
EMsg
of151
, I'm not sure what should be done, as my knowledge of SK2 internals is rather poor. It doesn't look like it's just a body definition. If we could get some basic support for sending and receiving packets, then later on we could write proper (new) handlers for that, such asSteamFriends
.It might be even already possible to parse and send those packets from within SK2, but I'm not sure how to achieve that. Please let me know if you do.
BTW, this chat is also fully supported in web browser - I also took a quick look and web browser achieves that with websocket connection to CM servers. Nothing interesting for us specifically probably, but worth mentioning.
The text was updated successfully, but these errors were encountered: