mirror of
https://github.com/Mrs4s/MiraiGo.git
synced 2025-05-04 11:07:40 +08:00
feat: support send Animated Stickers
This commit is contained in:
parent
1f8edc2de3
commit
aa92a8bb6c
@ -8,11 +8,11 @@ import (
|
||||
|
||||
const (
|
||||
UnknownDecoder = iota
|
||||
privateMsgDecoders
|
||||
nonSvcNotifyTroopSystemMsgDecoders
|
||||
troopSystemMsgDecoders
|
||||
sysMsgDecoders
|
||||
otherDecoders
|
||||
privateMsgDecoders
|
||||
)
|
||||
|
||||
func peekC2CDecoder(msgType int32) (decoder func(*QQClient, *msg.Message, *incomingPacketInfo), decoderType uint8) {
|
||||
|
@ -1,17 +1,16 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.14.0
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.17.1
|
||||
// source: pb/msg/msg.proto
|
||||
|
||||
package msg
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -7507,6 +7506,117 @@ func (x *PbPushMsg) GetBindUin() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type MsgElemInfoServtype37 struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Packid []byte `protobuf:"bytes,1,opt,name=packid" json:"packid,omitempty"`
|
||||
Stickerid []byte `protobuf:"bytes,2,opt,name=stickerid" json:"stickerid,omitempty"`
|
||||
Qsid *uint32 `protobuf:"varint,3,opt,name=qsid" json:"qsid,omitempty"`
|
||||
Sourcetype *uint32 `protobuf:"varint,4,opt,name=sourcetype" json:"sourcetype,omitempty"`
|
||||
Stickertype *uint32 `protobuf:"varint,5,opt,name=stickertype" json:"stickertype,omitempty"`
|
||||
Resultid []byte `protobuf:"bytes,6,opt,name=resultid" json:"resultid,omitempty"`
|
||||
Text []byte `protobuf:"bytes,7,opt,name=text" json:"text,omitempty"`
|
||||
Surpriseid []byte `protobuf:"bytes,8,opt,name=surpriseid" json:"surpriseid,omitempty"`
|
||||
Randomtype *uint32 `protobuf:"varint,9,opt,name=randomtype" json:"randomtype,omitempty"`
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) Reset() {
|
||||
*x = MsgElemInfoServtype37{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pb_msg_msg_proto_msgTypes[75]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*MsgElemInfoServtype37) ProtoMessage() {}
|
||||
|
||||
func (x *MsgElemInfoServtype37) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pb_msg_msg_proto_msgTypes[75]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use MsgElemInfoServtype37.ProtoReflect.Descriptor instead.
|
||||
func (*MsgElemInfoServtype37) Descriptor() ([]byte, []int) {
|
||||
return file_pb_msg_msg_proto_rawDescGZIP(), []int{75}
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) GetPackid() []byte {
|
||||
if x != nil {
|
||||
return x.Packid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) GetStickerid() []byte {
|
||||
if x != nil {
|
||||
return x.Stickerid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) GetQsid() uint32 {
|
||||
if x != nil && x.Qsid != nil {
|
||||
return *x.Qsid
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) GetSourcetype() uint32 {
|
||||
if x != nil && x.Sourcetype != nil {
|
||||
return *x.Sourcetype
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) GetStickertype() uint32 {
|
||||
if x != nil && x.Stickertype != nil {
|
||||
return *x.Stickertype
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) GetResultid() []byte {
|
||||
if x != nil {
|
||||
return x.Resultid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) GetText() []byte {
|
||||
if x != nil {
|
||||
return x.Text
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) GetSurpriseid() []byte {
|
||||
if x != nil {
|
||||
return x.Surpriseid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *MsgElemInfoServtype37) GetRandomtype() uint32 {
|
||||
if x != nil && x.Randomtype != nil {
|
||||
return *x.Randomtype
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ElemFlags2_Inst struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@ -7519,7 +7629,7 @@ type ElemFlags2_Inst struct {
|
||||
func (x *ElemFlags2_Inst) Reset() {
|
||||
*x = ElemFlags2_Inst{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_pb_msg_msg_proto_msgTypes[75]
|
||||
mi := &file_pb_msg_msg_proto_msgTypes[76]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -7532,7 +7642,7 @@ func (x *ElemFlags2_Inst) String() string {
|
||||
func (*ElemFlags2_Inst) ProtoMessage() {}
|
||||
|
||||
func (x *ElemFlags2_Inst) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_pb_msg_msg_proto_msgTypes[75]
|
||||
mi := &file_pb_msg_msg_proto_msgTypes[76]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -8746,11 +8856,29 @@ var file_pb_msg_msg_proto_rawDesc = []byte{
|
||||
0x12, 0x20, 0x0a, 0x0b, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6c, 0x61, 0x67, 0x18,
|
||||
0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x46, 0x6c,
|
||||
0x61, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x69, 0x6e, 0x64, 0x55, 0x69, 0x6e, 0x18, 0x0a, 0x20,
|
||||
0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x69, 0x6e, 0x64, 0x55, 0x69, 0x6e, 0x2a, 0x2e, 0x0a, 0x08,
|
||||
0x53, 0x79, 0x6e, 0x63, 0x46, 0x6c, 0x61, 0x67, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52,
|
||||
0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4e, 0x54, 0x49, 0x4e, 0x55, 0x4d, 0x45,
|
||||
0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x02, 0x42, 0x0c, 0x5a, 0x0a,
|
||||
0x70, 0x62, 0x2f, 0x6d, 0x73, 0x67, 0x3b, 0x6d, 0x73, 0x67,
|
||||
0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x69, 0x6e, 0x64, 0x55, 0x69, 0x6e, 0x22, 0x94, 0x02, 0x0a,
|
||||
0x16, 0x4d, 0x73, 0x67, 0x45, 0x6c, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x5f, 0x73, 0x65, 0x72,
|
||||
0x76, 0x74, 0x79, 0x70, 0x65, 0x33, 0x37, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x63, 0x6b, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x61, 0x63, 0x6b, 0x69, 0x64, 0x12,
|
||||
0x1c, 0x0a, 0x09, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x0c, 0x52, 0x09, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x69, 0x64, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x71, 0x73, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x71, 0x73, 0x69,
|
||||
0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x74, 0x79, 0x70, 0x65, 0x18,
|
||||
0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x74, 0x79, 0x70,
|
||||
0x65, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x74, 0x79, 0x70, 0x65,
|
||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x74,
|
||||
0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x64, 0x18,
|
||||
0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x69, 0x64, 0x12,
|
||||
0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74,
|
||||
0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x75, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x69,
|
||||
0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x75, 0x72, 0x70, 0x72, 0x69, 0x73,
|
||||
0x65, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x74, 0x79, 0x70,
|
||||
0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x74,
|
||||
0x79, 0x70, 0x65, 0x2a, 0x2e, 0x0a, 0x08, 0x53, 0x79, 0x6e, 0x63, 0x46, 0x6c, 0x61, 0x67, 0x12,
|
||||
0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f,
|
||||
0x4e, 0x54, 0x49, 0x4e, 0x55, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f,
|
||||
0x50, 0x10, 0x02, 0x42, 0x0c, 0x5a, 0x0a, 0x70, 0x62, 0x2f, 0x6d, 0x73, 0x67, 0x3b, 0x6d, 0x73,
|
||||
0x67,
|
||||
}
|
||||
|
||||
var (
|
||||
@ -8766,7 +8894,7 @@ func file_pb_msg_msg_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_pb_msg_msg_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_pb_msg_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 76)
|
||||
var file_pb_msg_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 77)
|
||||
var file_pb_msg_msg_proto_goTypes = []interface{}{
|
||||
(SyncFlag)(0), // 0: msg.SyncFlag
|
||||
(*GetMessageRequest)(nil), // 1: msg.GetMessageRequest
|
||||
@ -8844,7 +8972,8 @@ var file_pb_msg_msg_proto_goTypes = []interface{}{
|
||||
(*PbGetOneDayRoamMsgReq)(nil), // 73: msg.PbGetOneDayRoamMsgReq
|
||||
(*PbGetOneDayRoamMsgResp)(nil), // 74: msg.PbGetOneDayRoamMsgResp
|
||||
(*PbPushMsg)(nil), // 75: msg.PbPushMsg
|
||||
(*ElemFlags2_Inst)(nil), // 76: msg.ElemFlags2.Inst
|
||||
(*MsgElemInfoServtype37)(nil), // 76: msg.MsgElemInfo_servtype37
|
||||
(*ElemFlags2_Inst)(nil), // 77: msg.ElemFlags2.Inst
|
||||
}
|
||||
var file_pb_msg_msg_proto_depIdxs = []int32{
|
||||
0, // 0: msg.GetMessageRequest.syncFlag:type_name -> msg.SyncFlag
|
||||
@ -8894,8 +9023,8 @@ var file_pb_msg_msg_proto_depIdxs = []int32{
|
||||
45, // 44: msg.Elem.srcMsg:type_name -> msg.SourceMsg
|
||||
47, // 45: msg.Elem.lightApp:type_name -> msg.LightAppElem
|
||||
28, // 46: msg.Elem.commonElem:type_name -> msg.CommonElem
|
||||
76, // 47: msg.ElemFlags2.insts:type_name -> msg.ElemFlags2.Inst
|
||||
76, // 48: msg.ElemFlags2.srcInst:type_name -> msg.ElemFlags2.Inst
|
||||
77, // 47: msg.ElemFlags2.insts:type_name -> msg.ElemFlags2.Inst
|
||||
77, // 48: msg.ElemFlags2.srcInst:type_name -> msg.ElemFlags2.Inst
|
||||
27, // 49: msg.ElemFlags2.pcSupportDef:type_name -> msg.PcSupportDef
|
||||
30, // 50: msg.QQWalletMsg.aioBody:type_name -> msg.QQWalletAioBody
|
||||
31, // 51: msg.QQWalletAioBody.sender:type_name -> msg.QQWalletAioElem
|
||||
@ -9836,6 +9965,18 @@ func file_pb_msg_msg_proto_init() {
|
||||
}
|
||||
}
|
||||
file_pb_msg_msg_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*MsgElemInfoServtype37); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_pb_msg_msg_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ElemFlags2_Inst); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
@ -9854,7 +9995,7 @@ func file_pb_msg_msg_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_pb_msg_msg_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 76,
|
||||
NumMessages: 77,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
@ -855,3 +855,15 @@ message PbPushMsg {
|
||||
optional uint32 generalFlag = 9;
|
||||
optional uint64 bindUin = 10;
|
||||
}
|
||||
|
||||
message MsgElemInfo_servtype37 {
|
||||
optional bytes packid = 1;
|
||||
optional bytes stickerid = 2;
|
||||
optional uint32 qsid = 3;
|
||||
optional uint32 sourcetype = 4;
|
||||
optional uint32 stickertype = 5;
|
||||
optional bytes resultid = 6;
|
||||
optional bytes text = 7;
|
||||
optional bytes surpriseid = 8;
|
||||
optional uint32 randomtype = 9;
|
||||
}
|
||||
|
@ -101,6 +101,10 @@ type MusicShareElement struct {
|
||||
MusicUrl string // 音乐播放链接
|
||||
}
|
||||
|
||||
type AnimatedSticker struct {
|
||||
ID int32
|
||||
}
|
||||
|
||||
type RedBagMessageType int
|
||||
|
||||
// /com/tencent/mobileqq/data/MessageForQQWalletMsg.java
|
||||
@ -270,3 +274,7 @@ func (e *MusicShareElement) Type() ElementType {
|
||||
func (e *RedBagElement) Type() ElementType {
|
||||
return RedBag
|
||||
}
|
||||
|
||||
func (e *AnimatedSticker) Type() ElementType {
|
||||
return Face
|
||||
}
|
||||
|
@ -258,3 +258,20 @@ var faceMap = map[int]string{
|
||||
131: "街舞",
|
||||
276: "辣椒酱",
|
||||
}
|
||||
|
||||
var stickerMap = map[int]string{
|
||||
311: "1",
|
||||
312: "2",
|
||||
313: "3",
|
||||
314: "4",
|
||||
315: "5",
|
||||
316: "6",
|
||||
317: "7",
|
||||
318: "8",
|
||||
319: "9",
|
||||
320: "10",
|
||||
321: "11",
|
||||
324: "12",
|
||||
325: "14",
|
||||
326: "15",
|
||||
}
|
||||
|
@ -17,13 +17,20 @@ import (
|
||||
|
||||
const faceDownloadUrl = `https://down.qq.com/qqface/config/face_config2021071501.zip?mType=Other` //? 好像是会自动更新的
|
||||
|
||||
type ani struct {
|
||||
QSid string
|
||||
StickerID string
|
||||
}
|
||||
|
||||
type config struct {
|
||||
SystemFace []face `json:"sysface"`
|
||||
Stickers []ani `json:"-"`
|
||||
}
|
||||
|
||||
type face struct {
|
||||
QSid string `json:"QSid"`
|
||||
QDes string `json:"QDes"`
|
||||
AniStickerId string `json:"AniStickerId"`
|
||||
}
|
||||
|
||||
const codeTemplate = `// Code generated by message/generate.go DO NOT EDIT.
|
||||
@ -34,6 +41,11 @@ var faceMap = map[int]string{
|
||||
{{range .SystemFace}} {{.QSid}}: "{{.QDes}}",
|
||||
{{end}}
|
||||
}
|
||||
|
||||
var stickerMap = map[int]string{
|
||||
{{range .Stickers}} {{.QSid}}: "{{.StickerID}}",
|
||||
{{end}}
|
||||
}
|
||||
`
|
||||
|
||||
func main() {
|
||||
@ -50,8 +62,14 @@ func main() {
|
||||
data, _ := io.ReadAll(file)
|
||||
faceConfig := config{}
|
||||
_ = json.Unmarshal(data, &faceConfig)
|
||||
for i := range faceConfig.SystemFace {
|
||||
for i, sysface := range faceConfig.SystemFace {
|
||||
faceConfig.SystemFace[i].QDes = strings.TrimPrefix(faceConfig.SystemFace[i].QDes, "/")
|
||||
if sysface.AniStickerId != "" {
|
||||
faceConfig.Stickers = append(faceConfig.Stickers, ani{
|
||||
Qsid: sysface.QSid,
|
||||
StickerID: sysface.AniStickerId,
|
||||
})
|
||||
}
|
||||
}
|
||||
tmpl, _ := template.New("template").Parse(codeTemplate)
|
||||
buffer := &bytes.Buffer{}
|
||||
|
@ -2,6 +2,7 @@ package message
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
@ -152,3 +153,35 @@ func (e *ShortVideoElement) Pack() (r []*msg.Elem) {
|
||||
r = append(r, &msg.Elem{VideoFile: video})
|
||||
return
|
||||
}
|
||||
|
||||
func (e *AnimatedSticker) Pack() []*msg.Elem {
|
||||
name := "/" + faceMap[int(e.ID)]
|
||||
pbElem, _ := proto.Marshal(&msg.MsgElemInfoServtype37{
|
||||
Packid: []byte("1"),
|
||||
Stickerid: []byte(stickerMap[int(e.ID)]),
|
||||
Qsid: proto.Uint32(uint32(e.ID)),
|
||||
Sourcetype: proto.Uint32(1),
|
||||
Stickertype: proto.Uint32(1),
|
||||
Resultid: []byte{},
|
||||
Text: []byte(name),
|
||||
Surpriseid: []byte{},
|
||||
Randomtype: proto.Uint32(1),
|
||||
})
|
||||
common := &msg.Elem{
|
||||
CommonElem: &msg.CommonElem{
|
||||
ServiceType: proto.Int32(37),
|
||||
PbElem: pbElem,
|
||||
BusinessType: proto.Int32(1),
|
||||
},
|
||||
}
|
||||
|
||||
pbReverse, _ := proto.Marshal(&msg.Text{Str: proto.String(fmt.Sprintf("[%s]请使用最新版手机QQ体验新功能", faceMap[int(e.ID)]))})
|
||||
text := &msg.Elem{
|
||||
Text: &msg.Text{
|
||||
Str: &name,
|
||||
PbReserve: pbReverse,
|
||||
},
|
||||
}
|
||||
|
||||
return []*msg.Elem{common, text}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user