public static Richtext Parse(BinaryReader reader) { var result = new Richtext(); // TODO: 解析富文本 try { var messageType = reader.ReadByte(); var dataLength = reader.BeReadUInt16(); var pos = reader.BaseStream.Position; while (pos + dataLength < reader.BaseStream.Length) { reader.ReadByte(); switch (messageType) { case 0x01: // 纯文本消息、@ { var messageStr = reader.BeReadString(); if (messageStr.StartsWith("@") && pos + dataLength - reader.BaseStream.Position == 16) { reader.ReadBytes(10); result.Snippets.Add(new TextSnippet(messageStr, MessageType.At, ("Target", reader.BeReadLong32()))); } else { result.Snippets.Add(messageStr); } break; } case 0x02: // Emoji(系统表情) { reader.BeReadUInt16(); // 这里的数字貌似总是1:系统表情只有208个。 result.Snippets.Add(new TextSnippet("", MessageType.Emoji, ("Type", reader.ReadByte()))); break; } case 0x03: // 图片 { result.Snippets.Add(new TextSnippet(reader.BeReadString(), MessageType.Picture)); break; } case 0x0A: // 音频 { result.Snippets.Add(new TextSnippet(reader.BeReadString(), MessageType.Audio)); break; } case 0x0E: // 未知 { break; } case 0x12: // 群名片 { break; } case 0x14: // XML { reader.ReadByte(); result.Snippets.Add(new TextSnippet( GZipByteArray.DecompressString(reader.ReadBytes((int)(reader.BaseStream.Length - 1))), MessageType.Xml)); break; } case 0x18: // 群文件 { reader.ReadBytes(5); var fileName = reader.BeReadString(); // 文件名称... 长度总是一个byte reader.ReadByte(); reader.ReadBytes(reader.ReadByte()); // 文件大小 result.Snippets.Add(new TextSnippet(fileName, MessageType.OfflineFile)); break; } case 0x19: // 红包秘钥段 { if (reader.ReadByte() != 0xC2) { break; } reader.ReadBytes(19); reader.ReadBytes(reader.ReadByte()); // 恭喜发财 reader.ReadByte(); reader.ReadBytes(reader.ReadByte()); // 赶紧点击拆开吧 reader.ReadByte(); reader.ReadBytes(reader.ReadByte()); // QQ红包 reader.ReadBytes(5); reader.ReadBytes(reader.ReadByte()); // [QQ红包]恭喜发财 reader.ReadBytes(22); var redId = Encoding.UTF8.GetString(reader.ReadBytes(32)); //redid reader.ReadBytes(12); reader.ReadBytes(reader.BeReadUInt16()); reader.ReadBytes(0x10); var key1 = Encoding.UTF8.GetString(reader.ReadBytes(reader.ReadByte())); //Key1 reader.BeReadUInt16(); var key2 = Encoding.UTF8.GetString(reader.ReadBytes(reader.ReadByte())); //Key2 result.Snippets.Add(new TextSnippet("", MessageType.RedBag, ("RedId", redId), ("Key1", key1), ("Key2", key2))); break; } } reader.ReadBytes((int)(pos + dataLength - reader.BaseStream.Position)); messageType = reader.ReadByte(); dataLength = reader.BeReadUInt16(); pos = reader.BaseStream.Position; } } catch (Exception ex) { } // 移除所有空白的片段 result.Snippets.RemoveAll(s => s.Type == MessageType.Normal && string.IsNullOrEmpty(s.Content)); // 若长度大于1,那么应该只含有普通文本、At、表情、图片。 // 虽然我看着别人好像视频也能通过转发什么的弄进来,但是反正我们现在不支持接收音视频,所以不管了 return(result.Snippets.Count > 1 && result.Snippets.Any(s => s.Type != MessageType.Normal && s.Type != MessageType.At && s.Type != MessageType.Emoji && s.Type != MessageType.Picture) ? throw new NotSupportedException("富文本中包含多个非聊天代码") : result); }
public static List <byte[]> WriteSnippet(TextSnippet snippet, int length) { // TODO: 富文本支持 var ret = new List <byte[]>(); var bw = new BinaryWriter(new MemoryStream()); switch (snippet.Type) { case MessageType.Normal: { if (length + 6 >= 699) // 数字应该稍大点,但是我不清楚具体是多少 { length = 0; ret.Add(new byte[0]); } bw.BaseStream.Position = 6; foreach (var chr in snippet.Content) { var bytes = Encoding.UTF8.GetBytes(chr.ToString()); // 705 = 699 + 6个byte: (byte + short + byte + short) if (length + bw.BaseStream.Length + bytes.Length > 705) { var pos = bw.BaseStream.Position; bw.BaseStream.Position = 0; bw.Write(new byte[] { 0x01 }); bw.BeWrite((ushort)(pos - 3)); // 本来是+3和0的,但是提前预留了6个byte给它们,所以变成了-3和-6。下同理。 bw.Write(new byte[] { 0x01 }); bw.BeWrite((ushort)(pos - 6)); bw.BaseStream.Position = pos; ret.Add(bw.BaseStream.ToBytesArray()); bw = new BinaryWriter(new MemoryStream()); bw.BaseStream.Position = 6; length = 0; } bw.Write(bytes); } // 在最后一段的开头补充结构 { var pos = bw.BaseStream.Position; bw.BaseStream.Position = 0; bw.Write(new byte[] { 0x01 }); bw.BeWrite((ushort)(pos - 3)); bw.Write(new byte[] { 0x01 }); bw.BeWrite((ushort)(pos - 6)); bw.BaseStream.Position = pos; } break; } case MessageType.At: break; case MessageType.Emoji: { if (length + 12 > 699) { ret.Add(new byte[0]); } var faceIndex = Convert.ToByte(snippet.Content); if (faceIndex > 199) { faceIndex = 0; } bw.Write(new byte[] { 0x02, 0x00, 0x14, 0x01, 0x00, 0x01 }); bw.Write(faceIndex); bw.Write(new byte[] { 0xFF, 0x00, 0x02, 0x14 }); bw.Write((byte)(faceIndex + 65)); bw.Write(new byte[] { 0x0B, 0x00, 0x08, 0x00, 0x01, 0x00, 0x04, 0x52, 0xCC, 0x85, 0x50 }); break; } case MessageType.Picture: break; case MessageType.Xml: break; case MessageType.Json: break; case MessageType.Shake: break; case MessageType.Audio: break; case MessageType.Video: break; case MessageType.ExitGroup: break; case MessageType.GetGroupImformation: break; case MessageType.AddGroup: break; default: throw new ArgumentOutOfRangeException(); } if (bw.BaseStream.Position != 0) { ret.Add(bw.BaseStream.ToBytesArray()); } return(ret); }
public static Richtext Parse(BinaryReader reader) { var result = new Richtext(); // TODO: 解析富文本 try { var messageType = reader.ReadByte(); var dataLength = reader.BeReadChar(); var pos = reader.BaseStream.Position; while (pos + dataLength < reader.BaseStream.Length) { reader.ReadByte(); switch (messageType) { case 0x01: //文本消息 { var messageStr = Encoding.UTF8.GetString(reader.ReadBytes(reader.BeReadChar())); if (messageStr.Contains("@")) { //Reader.ReadBytes(10); //var AtQQ = Util.GetQQNumRetUint(Util.ToHex(Reader.ReadBytes(4)));//被At人的QQ号 result.Snippets.Add(new TextSnippet(messageStr, MessageType.At)); } else { result.Snippets.Add(messageStr); } break; } case 0x02: //小黄豆表情 { result.Snippets.Add(new TextSnippet( Util.GetQQNumRetUint(Util.ToHex(reader.ReadBytes(reader.BeReadChar()))).ToString(), MessageType.Emoji)); break; } case 0x03: //图片 { result.Snippets.Add(new TextSnippet( Encoding.UTF8.GetString(reader.ReadBytes(reader.BeReadChar())), MessageType.Picture)); break; } case 0x0A: //音频 { result.Snippets.Add(new TextSnippet(Encoding.UTF8.GetString(reader.ReadBytes(reader.BeReadChar())), MessageType.Audio)); break; } case 0x0E: //未知 { break; } case 0x12: //群名片 { break; } case 0x14: //XML { reader.ReadByte(); result.Snippets.Add(new TextSnippet(GZipByteArray.DecompressString(reader.ReadBytes((int)(reader.BaseStream.Length - 1))), MessageType.Xml)); break; } case 0x18: //群文件 { reader.ReadBytes(3); var fileName = reader.ReadBytes(reader.ReadByte()); //文件名称 reader.ReadByte(); reader.ReadBytes(reader.ReadByte()); //文件大小 result.Snippets.Add(new TextSnippet(Encoding.UTF8.GetString(fileName), MessageType.OfflineFile)); break; } case 0x19: //红包秘钥段 { if (reader.ReadByte() != 0xC2) { break; } reader.ReadBytes(19); reader.ReadBytes(reader.ReadByte()); //恭喜发财 reader.ReadByte(); reader.ReadBytes(reader.ReadByte()); //赶紧点击拆开吧 reader.ReadByte(); reader.ReadBytes(reader.ReadByte()); //QQ红包 reader.ReadBytes(5); reader.ReadBytes(reader.ReadByte()); //[QQ红包]恭喜发财 reader.ReadBytes(22); var redId = Encoding.UTF8.GetString(reader.ReadBytes(32)); //redid reader.ReadBytes(12); reader.ReadBytes(reader.BeReadChar()); reader.ReadBytes(0x10); var key1 = Encoding.UTF8.GetString(reader.ReadBytes(reader.ReadByte())); //Key1 reader.BeReadChar(); var key2 = Encoding.UTF8.GetString(reader.ReadBytes(reader.ReadByte())); //Key2 result.Snippets.Add(new TextSnippet("", MessageType.RedBag, ("RedId", redId), ("Key1", key1), ("Key2", key2))); break; } } reader.ReadBytes((int)(pos + dataLength - reader.BaseStream.Position)); messageType = reader.ReadByte(); dataLength = reader.BeReadChar(); pos = reader.BaseStream.Position; } } catch (Exception ex) { } return(result); }