Merge remote-tracking branch 'mirai/dev' into dev

This commit is contained in:
Him188 2021-02-07 14:18:35 +08:00
commit c4c0bce381
40 changed files with 1128 additions and 480 deletions

View File

@ -623,6 +623,7 @@ public abstract interface class net/mamoe/mirai/data/FriendInfo : net/mamoe/mira
public abstract fun getNick ()Ljava/lang/String;
public abstract fun getRemark ()Ljava/lang/String;
public abstract fun getUin ()J
public abstract fun setRemark (Ljava/lang/String;)V
}
public class net/mamoe/mirai/data/FriendInfoImpl : net/mamoe/mirai/data/FriendInfo {
@ -1914,7 +1915,7 @@ public final class net/mamoe/mirai/event/events/BotEventsKt {
public static final synthetic fun isSuccess (Lnet/mamoe/mirai/event/events/MessagePostSendEvent;)Z
}
public final class net/mamoe/mirai/event/events/BotGroupPermissionChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/BotGroupPermissionChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/contact/Group;Lnet/mamoe/mirai/contact/MemberPermission;Lnet/mamoe/mirai/contact/MemberPermission;)V
public final fun component1 ()Lnet/mamoe/mirai/contact/Group;
public final fun component2 ()Lnet/mamoe/mirai/contact/MemberPermission;
@ -1929,7 +1930,7 @@ public final class net/mamoe/mirai/event/events/BotGroupPermissionChangeEvent :
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/BotInvitedJoinGroupRequestEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/BotInvitedJoinGroupRequestEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BaseGroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/Bot;JJJLjava/lang/String;Ljava/lang/String;)V
public final synthetic fun accept ()Lkotlin/Unit;
public final fun accept ()V
@ -1945,7 +1946,7 @@ public final class net/mamoe/mirai/event/events/BotInvitedJoinGroupRequestEvent
public fun equals (Ljava/lang/Object;)Z
public fun getBot ()Lnet/mamoe/mirai/Bot;
public final fun getEventId ()J
public final fun getGroupId ()J
public fun getGroupId ()J
public final fun getGroupName ()Ljava/lang/String;
public final fun getInvitor ()Lnet/mamoe/mirai/contact/Friend;
public final fun getInvitorId ()J
@ -1957,7 +1958,7 @@ public final class net/mamoe/mirai/event/events/BotInvitedJoinGroupRequestEvent
public fun toString ()Ljava/lang/String;
}
public abstract class net/mamoe/mirai/event/events/BotJoinGroupEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupEvent, net/mamoe/mirai/internal/network/Packet {
public abstract class net/mamoe/mirai/event/events/BotJoinGroupEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public abstract fun getGroup ()Lnet/mamoe/mirai/contact/Group;
}
@ -1995,7 +1996,7 @@ public final class net/mamoe/mirai/event/events/BotJoinGroupEvent$Retrieve : net
public fun toString ()Ljava/lang/String;
}
public abstract class net/mamoe/mirai/event/events/BotLeaveEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/network/Packet {
public abstract class net/mamoe/mirai/event/events/BotLeaveEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun getBot ()Lnet/mamoe/mirai/Bot;
public abstract fun getGroup ()Lnet/mamoe/mirai/contact/Group;
}
@ -2025,7 +2026,7 @@ public final class net/mamoe/mirai/event/events/BotLeaveEvent$Kick : net/mamoe/m
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/BotMuteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/BotMuteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (ILnet/mamoe/mirai/contact/NormalMember;)V
public final fun component1 ()I
public final fun component2 ()Lnet/mamoe/mirai/contact/NormalMember;
@ -2077,6 +2078,7 @@ public abstract interface class net/mamoe/mirai/event/events/BotOfflineEvent$Cau
}
public final class net/mamoe/mirai/event/events/BotOfflineEvent$Dropped : net/mamoe/mirai/event/events/BotOfflineEvent, net/mamoe/mirai/event/events/BotOfflineEvent$CauseAware, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/Bot;Ljava/lang/Throwable;)V
public final fun component1 ()Lnet/mamoe/mirai/Bot;
public final fun component2 ()Ljava/lang/Throwable;
public final fun copy (Lnet/mamoe/mirai/Bot;Ljava/lang/Throwable;)Lnet/mamoe/mirai/event/events/BotOfflineEvent$Dropped;
@ -2091,6 +2093,7 @@ public final class net/mamoe/mirai/event/events/BotOfflineEvent$Dropped : net/ma
}
public final class net/mamoe/mirai/event/events/BotOfflineEvent$Force : net/mamoe/mirai/event/events/BotOfflineEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/Bot;Ljava/lang/String;Ljava/lang/String;)V
public final fun component1 ()Lnet/mamoe/mirai/Bot;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/lang/String;
@ -2107,6 +2110,7 @@ public final class net/mamoe/mirai/event/events/BotOfflineEvent$Force : net/mamo
}
public final class net/mamoe/mirai/event/events/BotOfflineEvent$MsfOffline : net/mamoe/mirai/event/events/BotOfflineEvent, net/mamoe/mirai/event/events/BotOfflineEvent$CauseAware, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/Bot;Ljava/lang/Throwable;)V
public final fun component1 ()Lnet/mamoe/mirai/Bot;
public final fun component2 ()Ljava/lang/Throwable;
public final fun copy (Lnet/mamoe/mirai/Bot;Ljava/lang/Throwable;)Lnet/mamoe/mirai/event/events/BotOfflineEvent$MsfOffline;
@ -2121,6 +2125,7 @@ public final class net/mamoe/mirai/event/events/BotOfflineEvent$MsfOffline : net
}
public final class net/mamoe/mirai/event/events/BotOfflineEvent$PacketFactoryErrorCode : net/mamoe/mirai/event/events/BotOfflineEvent, net/mamoe/mirai/event/events/BotOfflineEvent$CauseAware, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (ILnet/mamoe/mirai/Bot;Ljava/lang/Throwable;)V
public final fun component1 ()I
public final fun component2 ()Lnet/mamoe/mirai/Bot;
public final fun component3 ()Ljava/lang/Throwable;
@ -2137,6 +2142,7 @@ public final class net/mamoe/mirai/event/events/BotOfflineEvent$PacketFactoryErr
}
public final class net/mamoe/mirai/event/events/BotOfflineEvent$RequireReconnect : net/mamoe/mirai/event/events/BotOfflineEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/Bot;)V
public final fun component1 ()Lnet/mamoe/mirai/Bot;
public final fun copy (Lnet/mamoe/mirai/Bot;)Lnet/mamoe/mirai/event/events/BotOfflineEvent$RequireReconnect;
public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/BotOfflineEvent$RequireReconnect;Lnet/mamoe/mirai/Bot;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/BotOfflineEvent$RequireReconnect;
@ -2149,6 +2155,7 @@ public final class net/mamoe/mirai/event/events/BotOfflineEvent$RequireReconnect
}
public final class net/mamoe/mirai/event/events/BotOnlineEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotActiveEvent {
public fun <init> (Lnet/mamoe/mirai/Bot;)V
public final fun component1 ()Lnet/mamoe/mirai/Bot;
public final fun copy (Lnet/mamoe/mirai/Bot;)Lnet/mamoe/mirai/event/events/BotOnlineEvent;
public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/BotOnlineEvent;Lnet/mamoe/mirai/Bot;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/BotOnlineEvent;
@ -2162,6 +2169,7 @@ public abstract interface class net/mamoe/mirai/event/events/BotPassiveEvent : n
}
public final class net/mamoe/mirai/event/events/BotReloginEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotActiveEvent, net/mamoe/mirai/event/events/BotEvent {
public fun <init> (Lnet/mamoe/mirai/Bot;Ljava/lang/Throwable;)V
public final fun component1 ()Lnet/mamoe/mirai/Bot;
public final fun component2 ()Ljava/lang/Throwable;
public final fun copy (Lnet/mamoe/mirai/Bot;Ljava/lang/Throwable;)Lnet/mamoe/mirai/event/events/BotReloginEvent;
@ -2173,7 +2181,7 @@ public final class net/mamoe/mirai/event/events/BotReloginEvent : net/mamoe/mira
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/BotUnmuteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/BotUnmuteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/contact/NormalMember;)V
public final fun component1 ()Lnet/mamoe/mirai/contact/NormalMember;
public final fun copy (Lnet/mamoe/mirai/contact/NormalMember;)Lnet/mamoe/mirai/event/events/BotUnmuteEvent;
@ -2192,7 +2200,7 @@ public final class net/mamoe/mirai/event/events/EventCancelledException : java/l
public fun <init> (Ljava/lang/Throwable;)V
}
public final class net/mamoe/mirai/event/events/FriendAddEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/FriendEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/FriendAddEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/FriendEvent, net/mamoe/mirai/event/events/FriendInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/contact/Friend;)V
public final fun component1 ()Lnet/mamoe/mirai/contact/Friend;
public final fun copy (Lnet/mamoe/mirai/contact/Friend;)Lnet/mamoe/mirai/event/events/FriendAddEvent;
@ -2213,7 +2221,7 @@ public final class net/mamoe/mirai/event/events/FriendAvatarChangedEvent : net/m
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/FriendDeleteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/FriendEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/FriendDeleteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/FriendEvent, net/mamoe/mirai/event/events/FriendInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public final fun component1 ()Lnet/mamoe/mirai/contact/Friend;
public final fun copy (Lnet/mamoe/mirai/contact/Friend;)Lnet/mamoe/mirai/event/events/FriendDeleteEvent;
public static synthetic fun copy$default (Lnet/mamoe/mirai/event/events/FriendDeleteEvent;Lnet/mamoe/mirai/contact/Friend;ILjava/lang/Object;)Lnet/mamoe/mirai/event/events/FriendDeleteEvent;
@ -2308,7 +2316,7 @@ public final class net/mamoe/mirai/event/events/FriendMessageSyncEvent : net/mam
public fun getTime ()I
}
public final class net/mamoe/mirai/event/events/FriendNickChangedEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/FriendEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/FriendNickChangedEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/FriendEvent, net/mamoe/mirai/event/events/FriendInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public final fun component1 ()Lnet/mamoe/mirai/contact/Friend;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/lang/String;
@ -2322,7 +2330,7 @@ public final class net/mamoe/mirai/event/events/FriendNickChangedEvent : net/mam
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/FriendRemarkChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/FriendEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/FriendRemarkChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/FriendEvent, net/mamoe/mirai/event/events/FriendInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public final fun component1 ()Lnet/mamoe/mirai/contact/Friend;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Ljava/lang/String;
@ -2336,7 +2344,7 @@ public final class net/mamoe/mirai/event/events/FriendRemarkChangeEvent : net/ma
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/GroupAllowAnonymousChatEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/GroupAllowAnonymousChatEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (ZZLnet/mamoe/mirai/contact/Group;Lnet/mamoe/mirai/contact/NormalMember;)V
public final fun component1 ()Z
public final fun component2 ()Z
@ -2356,7 +2364,7 @@ public final class net/mamoe/mirai/event/events/GroupAllowAnonymousChatEvent : n
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/GroupAllowConfessTalkEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/GroupAllowConfessTalkEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (ZZLnet/mamoe/mirai/contact/Group;Z)V
public final fun component1 ()Z
public final fun component2 ()Z
@ -2375,7 +2383,7 @@ public final class net/mamoe/mirai/event/events/GroupAllowConfessTalkEvent : net
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/GroupAllowMemberInviteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/GroupAllowMemberInviteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (ZZLnet/mamoe/mirai/contact/Group;Lnet/mamoe/mirai/contact/NormalMember;)V
public final fun component1 ()Z
public final fun component2 ()Z
@ -2399,7 +2407,7 @@ public abstract interface class net/mamoe/mirai/event/events/GroupAwareMessageEv
public abstract fun getGroup ()Lnet/mamoe/mirai/contact/Group;
}
public final class net/mamoe/mirai/event/events/GroupEntranceAnnouncementChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/GroupEntranceAnnouncementChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lnet/mamoe/mirai/contact/Group;Lnet/mamoe/mirai/contact/NormalMember;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
@ -2497,7 +2505,7 @@ public final class net/mamoe/mirai/event/events/GroupMessageSyncEvent : net/mamo
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/GroupMuteAllEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/GroupMuteAllEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (ZZLnet/mamoe/mirai/contact/Group;Lnet/mamoe/mirai/contact/NormalMember;)V
public final fun component1 ()Z
public final fun component2 ()Z
@ -2517,7 +2525,7 @@ public final class net/mamoe/mirai/event/events/GroupMuteAllEvent : net/mamoe/mi
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/GroupNameChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/GroupNameChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/event/events/GroupSettingChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lnet/mamoe/mirai/contact/Group;Lnet/mamoe/mirai/contact/NormalMember;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
@ -2670,7 +2678,7 @@ public final class net/mamoe/mirai/event/events/ImageUploadEvent$Succeed : net/m
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/MemberCardChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/MemberCardChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lnet/mamoe/mirai/contact/NormalMember;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
@ -2719,7 +2727,7 @@ public final class net/mamoe/mirai/event/events/MemberHonorChangeEvent$Lose : ne
public fun toString ()Ljava/lang/String;
}
public abstract class net/mamoe/mirai/event/events/MemberJoinEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/internal/network/Packet {
public abstract class net/mamoe/mirai/event/events/MemberJoinEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public synthetic fun <init> (Lnet/mamoe/mirai/contact/NormalMember;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun getMember ()Lnet/mamoe/mirai/contact/Member;
public fun getMember ()Lnet/mamoe/mirai/contact/NormalMember;
@ -2763,7 +2771,7 @@ public final class net/mamoe/mirai/event/events/MemberJoinEvent$Retrieve : net/m
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/MemberJoinRequestEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/MemberJoinRequestEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BaseGroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/network/Packet {
public static final field Companion Lnet/mamoe/mirai/event/events/MemberJoinRequestEvent$Companion;
public synthetic fun <init> (Lnet/mamoe/mirai/Bot;JLjava/lang/String;JJLjava/lang/String;Ljava/lang/String;)V
public fun <init> (Lnet/mamoe/mirai/Bot;JLjava/lang/String;JJLjava/lang/String;Ljava/lang/String;Ljava/lang/Long;)V
@ -2789,7 +2797,7 @@ public final class net/mamoe/mirai/event/events/MemberJoinRequestEvent : net/mam
public final fun getFromId ()J
public final fun getFromNick ()Ljava/lang/String;
public final fun getGroup ()Lnet/mamoe/mirai/contact/Group;
public final fun getGroupId ()J
public fun getGroupId ()J
public final fun getGroupName ()Ljava/lang/String;
public final fun getInvitor ()Lnet/mamoe/mirai/contact/NormalMember;
public final fun getInvitorId ()Ljava/lang/Long;
@ -2812,7 +2820,7 @@ public final class net/mamoe/mirai/event/events/MemberJoinRequestEvent : net/mam
public fun toString ()Ljava/lang/String;
}
public abstract class net/mamoe/mirai/event/events/MemberLeaveEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent {
public abstract class net/mamoe/mirai/event/events/MemberLeaveEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent {
}
public final class net/mamoe/mirai/event/events/MemberLeaveEvent$Kick : net/mamoe/mirai/event/events/MemberLeaveEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/internal/network/Packet {
@ -2842,7 +2850,7 @@ public final class net/mamoe/mirai/event/events/MemberLeaveEvent$Quit : net/mamo
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/MemberMuteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/MemberMuteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/contact/Member;ILnet/mamoe/mirai/contact/Member;)V
public final fun component1 ()Lnet/mamoe/mirai/contact/Member;
public final fun component2 ()I
@ -2857,7 +2865,7 @@ public final class net/mamoe/mirai/event/events/MemberMuteEvent : net/mamoe/mira
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/MemberPermissionChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/MemberPermissionChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotPassiveEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/contact/NormalMember;Lnet/mamoe/mirai/contact/MemberPermission;Lnet/mamoe/mirai/contact/MemberPermission;)V
public final fun component1 ()Lnet/mamoe/mirai/contact/NormalMember;
public final fun component2 ()Lnet/mamoe/mirai/contact/MemberPermission;
@ -2873,7 +2881,7 @@ public final class net/mamoe/mirai/event/events/MemberPermissionChangeEvent : ne
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/MemberSpecialTitleChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupOperableEvent {
public final class net/mamoe/mirai/event/events/MemberSpecialTitleChangeEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lnet/mamoe/mirai/contact/NormalMember;Lnet/mamoe/mirai/contact/NormalMember;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
@ -2892,7 +2900,7 @@ public final class net/mamoe/mirai/event/events/MemberSpecialTitleChangeEvent :
public fun toString ()Ljava/lang/String;
}
public final class net/mamoe/mirai/event/events/MemberUnmuteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/MemberUnmuteEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/GroupMemberEvent, net/mamoe/mirai/event/events/GroupMemberInfoChangeEvent, net/mamoe/mirai/event/events/GroupOperableEvent, net/mamoe/mirai/internal/network/Packet {
public fun <init> (Lnet/mamoe/mirai/contact/Member;Lnet/mamoe/mirai/contact/Member;)V
public final fun component1 ()Lnet/mamoe/mirai/contact/Member;
public final fun component2 ()Lnet/mamoe/mirai/contact/Member;
@ -2991,7 +2999,7 @@ public final class net/mamoe/mirai/event/events/MessageRecallEvent$GroupRecall :
public abstract interface class net/mamoe/mirai/event/events/MessageSyncEvent : net/mamoe/mirai/event/events/MessageEvent {
}
public final class net/mamoe/mirai/event/events/NewFriendRequestEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/internal/network/Packet {
public final class net/mamoe/mirai/event/events/NewFriendRequestEvent : net/mamoe/mirai/event/AbstractEvent, net/mamoe/mirai/event/events/BotEvent, net/mamoe/mirai/event/events/FriendInfoChangeEvent, net/mamoe/mirai/internal/network/Packet {
public final synthetic fun accept ()Lkotlin/Unit;
public final fun accept ()V
public final fun accept (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@ -5385,6 +5393,9 @@ public class net/mamoe/mirai/utils/BotConfiguration {
public fun <init> ()V
public final fun autoReconnectOnForceOffline ()V
public final fun copy ()Lnet/mamoe/mirai/utils/BotConfiguration;
public final fun disableContactCaches ()V
public final fun disableFriendListCache ()V
public final fun disableGroupMemberListCache ()V
public final fun fileBasedDeviceInfo ()V
public final fun fileBasedDeviceInfo (Ljava/lang/String;)V
public static synthetic fun fileBasedDeviceInfo$default (Lnet/mamoe/mirai/utils/BotConfiguration;Ljava/lang/String;ILjava/lang/Object;)V
@ -5393,6 +5404,8 @@ public class net/mamoe/mirai/utils/BotConfiguration {
public static final fun getDefault ()Lnet/mamoe/mirai/utils/BotConfiguration;
public final fun getDeviceInfo ()Lkotlin/jvm/functions/Function1;
public final fun getFirstReconnectDelayMillis ()J
public final fun getFriendListCache ()Lnet/mamoe/mirai/utils/BotConfiguration$FriendListCache;
public final fun getGroupMemberListCache ()Lnet/mamoe/mirai/utils/BotConfiguration$GroupMemberListCache;
public final fun getHeartbeatPeriodMillis ()J
public final fun getHeartbeatTimeoutMillis ()J
public final fun getHighwayUploadCoroutineCount ()I
@ -5431,6 +5444,8 @@ public class net/mamoe/mirai/utils/BotConfiguration {
public final fun setBotLoggerSupplier (Lkotlin/jvm/functions/Function1;)V
public final fun setDeviceInfo (Lkotlin/jvm/functions/Function1;)V
public final fun setFirstReconnectDelayMillis (J)V
public final fun setFriendListCache (Lnet/mamoe/mirai/utils/BotConfiguration$FriendListCache;)V
public final fun setGroupMemberListCache (Lnet/mamoe/mirai/utils/BotConfiguration$GroupMemberListCache;)V
public final fun setHeartbeatPeriodMillis (J)V
public final fun setHeartbeatTimeoutMillis (J)V
public final fun setHighwayUploadCoroutineCount (I)V
@ -5451,6 +5466,24 @@ public final class net/mamoe/mirai/utils/BotConfiguration$Companion {
public abstract interface annotation class net/mamoe/mirai/utils/BotConfiguration$ConfigurationDsl : java/lang/annotation/Annotation {
}
public final class net/mamoe/mirai/utils/BotConfiguration$FriendListCache {
public fun <init> ()V
public fun <init> (Ljava/io/File;)V
public fun <init> (Ljava/io/File;J)V
public synthetic fun <init> (Ljava/io/File;JILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getCacheFile ()Ljava/io/File;
public final fun getSaveIntervalMillis ()J
}
public final class net/mamoe/mirai/utils/BotConfiguration$GroupMemberListCache {
public fun <init> ()V
public fun <init> (Ljava/io/File;)V
public fun <init> (Ljava/io/File;J)V
public synthetic fun <init> (Ljava/io/File;JILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getCacheDir ()Ljava/io/File;
public final fun getSaveIntervalMillis ()J
}
public final class net/mamoe/mirai/utils/BotConfiguration$MiraiProtocol : java/lang/Enum {
public static final field ANDROID_PAD Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;
public static final field ANDROID_PHONE Lnet/mamoe/mirai/utils/BotConfiguration$MiraiProtocol;

0
gradlew vendored Normal file → Executable file
View File

0
gradlew.bat vendored Normal file → Executable file
View File

View File

@ -11,6 +11,7 @@
package net.mamoe.mirai.contact
import kotlinx.serialization.Serializable
import net.mamoe.mirai.Bot
import kotlin.internal.InlineOnly

View File

@ -17,9 +17,16 @@ public interface FriendInfo : UserInfo {
public override val nick: String
public override val remark: String
public override var remark: String
}
@Deprecated(
"Moved to net.mamoe.mirai.internal.contact.FriendInfoImpl. Kept for binary compatibility.",
ReplaceWith("FriendInfoImpl", "net.mamoe.mirai.internal.contact.FriendInfoImpl"),
level = DeprecationLevel.HIDDEN
)
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@LowLevelApi
public open class FriendInfoImpl(
override val uin: Long,

View File

@ -25,7 +25,7 @@ import net.mamoe.mirai.utils.MiraiInternalApi
/**
* [Bot] 登录完成, 好友列表, 群组列表初始化完成
*/
public data class BotOnlineEvent internal constructor(
public data class BotOnlineEvent @MiraiInternalApi public constructor(
public override val bot: Bot
) : BotActiveEvent, AbstractEvent()
@ -55,7 +55,7 @@ public sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
/**
* 被挤下线. 默认不会自动重连. 可将 [reconnect] 改为 `true` 以重连.
*/
public data class Force internal constructor(
public data class Force @MiraiInternalApi public constructor(
public override val bot: Bot,
public val title: String,
public val message: String,
@ -67,7 +67,7 @@ public sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
* 被服务器断开
*/
@MiraiInternalApi("This is very experimental and might be changed")
public data class MsfOffline internal constructor(
public data class MsfOffline @MiraiInternalApi public constructor(
public override val bot: Bot,
public override val cause: Throwable?
) : BotOfflineEvent(), Packet, BotPassiveEvent, CauseAware {
@ -77,7 +77,7 @@ public sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
/**
* 因网络问题而掉线
*/
public data class Dropped internal constructor(
public data class Dropped @MiraiInternalApi public constructor(
public override val bot: Bot,
public override val cause: Throwable?
) : BotOfflineEvent(), Packet, BotPassiveEvent, CauseAware {
@ -88,7 +88,7 @@ public sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
* returnCode = -10008 等原因掉线
*/
@MiraiInternalApi("This is very experimental and might be changed")
public data class PacketFactoryErrorCode internal constructor(
public data class PacketFactoryErrorCode @MiraiInternalApi public constructor(
val returnCode: Int,
public override val bot: Bot,
public override val cause: Throwable
@ -100,7 +100,7 @@ public sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
* 服务器主动要求更换另一个服务器
*/
@MiraiInternalApi
public data class RequireReconnect internal constructor(
public data class RequireReconnect @MiraiInternalApi public constructor(
public override val bot: Bot
) : BotOfflineEvent(), Packet, BotPassiveEvent {
override var reconnect: Boolean = true
@ -115,7 +115,7 @@ public sealed class BotOfflineEvent : BotEvent, AbstractEvent() {
/**
* [Bot] 主动或被动重新登录. 在此事件广播前就已经登录完毕.
*/
public data class BotReloginEvent internal constructor(
public data class BotReloginEvent @MiraiInternalApi public constructor(
public override val bot: Bot,
public val cause: Throwable?
) : BotEvent, BotActiveEvent, AbstractEvent()

View File

@ -32,7 +32,7 @@ public data class FriendRemarkChangeEvent internal constructor(
public override val friend: Friend,
public val oldRemark: String,
public val newRemark: String
) : FriendEvent, Packet, AbstractEvent()
) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
/**
* 成功添加了一个新好友的事件
@ -42,14 +42,14 @@ public data class FriendAddEvent @MiraiInternalApi constructor(
* 新好友. 已经添加到 [Bot.friends]
*/
public override val friend: Friend
) : FriendEvent, Packet, AbstractEvent()
) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
/**
* 好友已被删除或主动删除的事件.
*/
public data class FriendDeleteEvent internal constructor(
public override val friend: Friend
) : FriendEvent, Packet, AbstractEvent()
) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
/**
* 一个账号请求添加机器人为好友的事件
@ -77,7 +77,7 @@ public data class NewFriendRequestEvent internal constructor(
* 群名片或好友昵称
*/
public val fromNick: String
) : BotEvent, Packet, AbstractEvent() {
) : BotEvent, Packet, AbstractEvent(), FriendInfoChangeEvent {
@JvmField
internal val responded: AtomicBoolean = AtomicBoolean(false)
@ -109,7 +109,7 @@ public data class FriendNickChangedEvent internal constructor(
public override val friend: Friend,
public val from: String,
public val to: String
) : FriendEvent, Packet, AbstractEvent()
) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
/**
* 好友输入状态改变的事件当开始输入文字退出聊天窗口或清空输入框时会触发此事件

View File

@ -9,7 +9,9 @@
@file:JvmMultifileClass
@file:JvmName("BotEventsKt")
@file:Suppress("unused", "FunctionName", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "DEPRECATION_ERROR")
@file:Suppress("unused", "FunctionName", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "DEPRECATION_ERROR",
"MemberVisibilityCanBePrivate"
)
package net.mamoe.mirai.event.events
@ -28,8 +30,8 @@ import java.util.concurrent.atomic.AtomicBoolean
/**
* 机器人被踢出群或在其他客户端主动退出一个群. 在事件广播前 [Bot.groups] 就已删除这个群.
*/
public sealed class BotLeaveEvent : BotEvent, Packet, AbstractEvent() {
public abstract val group: Group
public sealed class BotLeaveEvent : BotEvent, Packet, AbstractEvent(), GroupMemberInfoChangeEvent {
public abstract override val group: Group
/**
* 机器人主动退出一个群.
@ -64,7 +66,7 @@ public data class BotGroupPermissionChangeEvent @MiraiInternalApi constructor(
public override val group: Group,
public val origin: MemberPermission,
public val new: MemberPermission
) : BotPassiveEvent, GroupEvent, Packet, AbstractEvent()
) : BotPassiveEvent, GroupEvent, Packet, AbstractEvent(), GroupMemberInfoChangeEvent
/**
* Bot 被禁言
@ -75,7 +77,7 @@ public data class BotMuteEvent @MiraiInternalApi constructor(
* 操作人.
*/
public val operator: NormalMember
) : GroupEvent, Packet, BotPassiveEvent, AbstractEvent() {
) : GroupEvent, Packet, BotPassiveEvent, AbstractEvent(), GroupMemberInfoChangeEvent {
public override val group: Group
get() = operator.group
}
@ -88,7 +90,7 @@ public data class BotUnmuteEvent @MiraiInternalApi constructor(
* 操作人.
*/
public val operator: NormalMember
) : GroupEvent, Packet, BotPassiveEvent, AbstractEvent() {
) : GroupEvent, Packet, BotPassiveEvent, AbstractEvent(), GroupMemberInfoChangeEvent {
public override val group: Group
get() = operator.group
}
@ -96,7 +98,7 @@ public data class BotUnmuteEvent @MiraiInternalApi constructor(
/**
* Bot 成功加入了一个新群
*/
public sealed class BotJoinGroupEvent : GroupEvent, BotPassiveEvent, Packet, AbstractEvent() {
public sealed class BotJoinGroupEvent : GroupEvent, BotPassiveEvent, Packet, AbstractEvent(), GroupMemberInfoChangeEvent {
public abstract override val group: Group
/**
@ -164,7 +166,7 @@ public data class GroupNameChangeEvent @MiraiInternalApi constructor(
* 操作人. null 时则是机器人操作
*/
public override val operator: NormalMember?
) : GroupSettingChangeEvent<String>, Packet, GroupOperableEvent, AbstractEvent()
) : GroupSettingChangeEvent<String>, Packet, GroupOperableEvent, AbstractEvent(), GroupMemberInfoChangeEvent
/**
* 入群公告改变. 此事件广播前修改就已经完成.
@ -177,7 +179,7 @@ public data class GroupEntranceAnnouncementChangeEvent @MiraiInternalApi constru
* 操作人. null 时则是机器人操作
*/
public override val operator: NormalMember?
) : GroupSettingChangeEvent<String>, Packet, GroupOperableEvent, AbstractEvent()
) : GroupSettingChangeEvent<String>, Packet, GroupOperableEvent, AbstractEvent(), GroupMemberInfoChangeEvent
/**
@ -191,7 +193,7 @@ public data class GroupMuteAllEvent @MiraiInternalApi constructor(
* 操作人. null 时则是机器人操作
*/
public override val operator: NormalMember?
) : GroupSettingChangeEvent<Boolean>, Packet, GroupOperableEvent, AbstractEvent()
) : GroupSettingChangeEvent<Boolean>, Packet, GroupOperableEvent, AbstractEvent(), GroupMemberInfoChangeEvent
/**
@ -205,7 +207,7 @@ public data class GroupAllowAnonymousChatEvent @MiraiInternalApi constructor(
* 操作人. null 时则是机器人操作
*/
public override val operator: NormalMember?
) : GroupSettingChangeEvent<Boolean>, Packet, GroupOperableEvent, AbstractEvent()
) : GroupSettingChangeEvent<Boolean>, Packet, GroupOperableEvent, AbstractEvent(), GroupMemberInfoChangeEvent
/**
@ -216,7 +218,7 @@ public data class GroupAllowConfessTalkEvent @MiraiInternalApi constructor(
public override val new: Boolean,
public override val group: Group,
public val isByBot: Boolean // 无法获取操作人
) : GroupSettingChangeEvent<Boolean>, Packet, AbstractEvent()
) : GroupSettingChangeEvent<Boolean>, Packet, AbstractEvent(), GroupMemberInfoChangeEvent
/**
* "允许群员邀请好友加群" 功能状态改变. 此事件广播前修改就已经完成.
@ -229,7 +231,7 @@ public data class GroupAllowMemberInviteEvent @MiraiInternalApi constructor(
* 操作人. null 时则是机器人操作
*/
public override val operator: NormalMember?
) : GroupSettingChangeEvent<Boolean>, Packet, GroupOperableEvent, AbstractEvent()
) : GroupSettingChangeEvent<Boolean>, Packet, GroupOperableEvent, AbstractEvent(), GroupMemberInfoChangeEvent
// endregion
@ -245,7 +247,7 @@ public data class GroupAllowMemberInviteEvent @MiraiInternalApi constructor(
public sealed class MemberJoinEvent(
public override val member: NormalMember
) : GroupMemberEvent, BotPassiveEvent, Packet,
AbstractEvent() {
AbstractEvent(), GroupMemberInfoChangeEvent {
/**
* 被邀请加入群
*/
@ -282,7 +284,7 @@ public sealed class MemberJoinEvent(
/**
* 成员已经离开群的事件. 在事件广播前成员就已经从 [Group.members] 中删除
*/
public sealed class MemberLeaveEvent : GroupMemberEvent, AbstractEvent() {
public sealed class MemberLeaveEvent : GroupMemberEvent, AbstractEvent(), GroupMemberInfoChangeEvent {
/**
* 成员被踢出群. 成员不可能是机器人自己.
*/
@ -320,13 +322,13 @@ public data class BotInvitedJoinGroupRequestEvent @MiraiInternalApi constructor(
* 邀请入群的账号的 id
*/
public val invitorId: Long,
public val groupId: Long,
public override val groupId: Long,
public val groupName: String,
/**
* 邀请人昵称
*/
public val invitorNick: String
) : BotEvent, Packet, AbstractEvent() {
) : BotEvent, Packet, AbstractEvent(), BaseGroupMemberInfoChangeEvent {
/**
* 邀请人. 若在事件发生后邀请人已经被删除好友, [invitor] `null`.
*/
@ -360,7 +362,7 @@ public data class MemberJoinRequestEvent @MiraiInternalApi constructor(
* 申请入群的账号的 id
*/
val fromId: Long,
val groupId: Long,
override val groupId: Long,
val groupName: String,
/**
* 申请人昵称
@ -370,7 +372,7 @@ public data class MemberJoinRequestEvent @MiraiInternalApi constructor(
* 邀请人 id如果是邀请入群
*/
val invitorId: Long? = null
) : BotEvent, Packet, AbstractEvent() {
) : BotEvent, Packet, AbstractEvent(), BaseGroupMemberInfoChangeEvent {
/**
* 相关群. 若在事件发生后机器人退出这个群, [group] `null`.
*/
@ -471,7 +473,7 @@ public data class MemberCardChangeEvent @MiraiInternalApi constructor(
public val new: String,
public override val member: NormalMember
) : GroupMemberEvent, Packet, AbstractEvent()
) : GroupMemberEvent, Packet, AbstractEvent(), GroupMemberInfoChangeEvent
/**
* 成员群头衔改动. 一定为群主操作
@ -495,7 +497,7 @@ public data class MemberSpecialTitleChangeEvent @MiraiInternalApi constructor(
* null 时则是机器人操作.
*/
public override val operator: NormalMember?
) : GroupMemberEvent, GroupOperableEvent, AbstractEvent()
) : GroupMemberEvent, GroupOperableEvent, AbstractEvent(), GroupMemberInfoChangeEvent
// endregion
@ -509,7 +511,7 @@ public data class MemberPermissionChangeEvent @MiraiInternalApi constructor(
public override val member: NormalMember,
public val origin: MemberPermission,
public val new: MemberPermission
) : GroupMemberEvent, BotPassiveEvent, Packet, AbstractEvent()
) : GroupMemberEvent, BotPassiveEvent, Packet, AbstractEvent(), GroupMemberInfoChangeEvent
// endregion
@ -528,7 +530,7 @@ public data class MemberMuteEvent @MiraiInternalApi constructor(
* 操作人. null 则为机器人操作
*/
public override val operator: Member?
) : GroupMemberEvent, Packet, GroupOperableEvent, AbstractEvent()
) : GroupMemberEvent, Packet, GroupOperableEvent, AbstractEvent(), GroupMemberInfoChangeEvent
/**
* 群成员被取消禁言事件. 被禁言的成员都不可能是机器人本人
@ -541,7 +543,7 @@ public data class MemberUnmuteEvent @MiraiInternalApi constructor(
* 操作人. null 则为机器人操作
*/
public override val operator: Member?
) : GroupMemberEvent, Packet, GroupOperableEvent, AbstractEvent()
) : GroupMemberEvent, Packet, GroupOperableEvent, AbstractEvent(), GroupMemberInfoChangeEvent
// endregion

View File

@ -16,6 +16,7 @@ import net.mamoe.mirai.Bot
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.Event
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.utils.MiraiInternalApi
/**
* 有关一个 [Bot] 的事件
@ -90,6 +91,8 @@ public interface FriendEvent : BotEvent, UserEvent {
override val user: Friend get() = friend
}
internal interface FriendInfoChangeEvent : BotEvent // for cache
/**
* 有关陌生人的事件
*/
@ -108,6 +111,19 @@ public interface GroupMemberEvent : GroupEvent, UserEvent {
override val user: Member get() = member
}
/**
* 用于更新缓存, 请勿使用.
*/
@MiraiInternalApi
internal interface BaseGroupMemberInfoChangeEvent : BotEvent {
val groupId: Long
} // for cache
@MiraiInternalApi
internal interface GroupMemberInfoChangeEvent : BotEvent, GroupEvent, BaseGroupMemberInfoChangeEvent {
override val groupId: Long get() = group.id
} // for cache
public interface OtherClientEvent : BotEvent, Packet {
public val client: OtherClient
override val bot: Bot get() = client.bot

View File

@ -57,32 +57,87 @@ public open class BotConfiguration { // open for Java
public var workingDir: File = File(".")
/**
* 日志记录器
*
* - 默认打印到标准输出, 通过 [MiraiLogger.create]
* - 忽略所有日志: [noBotLog]
* - 重定向到一个目录: `networkLoggerSupplier = { DirectoryLogger("Net ${it.id}") }`
* - 重定向到一个文件: `networkLoggerSupplier = { SingleFileLogger("Net ${it.id}") }`
*
* @see MiraiLogger
* Json 序列化器, 使用 'kotlinx.serialization'
*/
public var botLoggerSupplier: ((Bot) -> MiraiLogger) = { MiraiLogger.create("Bot ${it.id}") }
@MiraiExperimentalApi
public var json: Json = kotlin.runCatching {
Json {
isLenient = true
ignoreUnknownKeys = true
prettyPrint = true
}
}.getOrElse { Json {} }
/**
* 网络层日志构造器
*
* - 默认打印到标准输出, 通过 [MiraiLogger.create]
* - 忽略所有日志: [noNetworkLog]
* - 重定向到一个目录: `networkLoggerSupplier = { DirectoryLogger("Net ${it.id}") }`
* - 重定向到一个文件: `networkLoggerSupplier = { SingleFileLogger("Net ${it.id}") }`
*
* @see MiraiLogger
*/
public var networkLoggerSupplier: ((Bot) -> MiraiLogger) = { MiraiLogger.create("Net ${it.id}") }
///////////////////////////////////////////////////////////////////////////
// Coroutines
///////////////////////////////////////////////////////////////////////////
/** 父 [CoroutineContext]. [Bot] 创建后会使用 [SupervisorJob] 覆盖其 [Job], 但会将这个 [Job] 作为父 [Job] */
public var parentCoroutineContext: CoroutineContext = EmptyCoroutineContext
/**
* 使用当前协程的 [coroutineContext] 作为 [parentCoroutineContext].
*
* Bot 将会使用一个 [SupervisorJob] 覆盖 [coroutineContext] 当前协程的 [Job], 并使用当前协程的 [Job] 作为父 [Job]
*
* 用例:
* ```
* coroutineScope {
* val bot = Bot(...) {
* inheritCoroutineContext()
* }
* bot.login()
* } // coroutineScope 会等待 Bot 退出
* ```
*
*
* **注意**: `bot.cancel` 时将会让父 [Job] 也被 cancel.
* ```
* coroutineScope { // this: CoroutineScope
* launch {
* while(isActive) {
* delay(500)
* println("I'm alive")
* }
* }
*
* val bot = Bot(...) {
* inheritCoroutineContext() // 使用 `coroutineScope` 的 Job 作为父 Job
* }
* bot.login()
* bot.cancel() // 取消了整个 `coroutineScope`, 因此上文不断打印 `"I'm alive"` 的协程也会被取消.
* }
* ```
*
* 因此, 此函数尤为适合在 `suspend fun main()` 中使用, 它能阻止主线程退出:
* ```
* suspend fun main() {
* val bot = Bot() {
* inheritCoroutineContext()
* }
* bot.subscribe { ... }
*
* // 主线程不会退出, 直到 Bot 离线.
* }
* ```
*
* 简言之,
* - 若想让 [Bot] 作为 '守护进程' 运行, 则无需调用 [inheritCoroutineContext].
* - 若想让 [Bot] 依赖于当前协程, 让当前协程等待 [Bot] 运行, 则使用 [inheritCoroutineContext]
*
* @see parentCoroutineContext
*/
@JvmSynthetic
@ConfigurationDsl
public suspend inline fun inheritCoroutineContext() {
parentCoroutineContext = coroutineContext
}
///////////////////////////////////////////////////////////////////////////
// Connection
///////////////////////////////////////////////////////////////////////////
/** 心跳周期. 过长会导致被服务器断开连接. */
public var heartbeatPeriodMillis: Long = 60.secondsToMillis
@ -125,6 +180,26 @@ public open class BotConfiguration { // open for Java
/** 使用协议类型 */
public var protocol: MiraiProtocol = MiraiProtocol.ANDROID_PHONE
public enum class MiraiProtocol {
/**
* Android 手机. 所有功能都支持.
*/
ANDROID_PHONE,
/**
* Android 平板.
*
* 注意: 不支持戳一戳事件解析
*/
ANDROID_PAD,
/**
* Android 手表.
*/
ANDROID_WATCH,
}
/**
* Highway 通道上传图片, 语音, 文件等资源时的协程数量.
*
@ -135,25 +210,6 @@ public open class BotConfiguration { // open for Java
*/
public var highwayUploadCoroutineCount: Int = Runtime.getRuntime().availableProcessors()
/**
* 设备信息覆盖. 在没有手动指定时将会通过日志警告, 并使用随机设备信息.
* @see fileBasedDeviceInfo 使用指定文件存储设备信息
* @see randomDeviceInfo 使用随机设备信息
*/
public var deviceInfo: ((Bot) -> DeviceInfo)? = deviceInfoStub // allows user to set `null` manually.
/**
* Json 序列化器, 使用 'kotlinx.serialization'
*/
@MiraiExperimentalApi
public var json: Json = kotlin.runCatching {
Json {
isLenient = true
ignoreUnknownKeys = true
prettyPrint = true
}
}.getOrElse { Json {} }
/**
* 设置 [autoReconnectOnForceOffline] `true`, 即在被挤下线时自动重连.
* @since 2.1
@ -163,6 +219,17 @@ public open class BotConfiguration { // open for Java
autoReconnectOnForceOffline = true
}
///////////////////////////////////////////////////////////////////////////
// Device
///////////////////////////////////////////////////////////////////////////
/**
* 设备信息覆盖. 在没有手动指定时将会通过日志警告, 并使用随机设备信息.
* @see fileBasedDeviceInfo 使用指定文件存储设备信息
* @see randomDeviceInfo 使用随机设备信息
*/
public var deviceInfo: ((Bot) -> DeviceInfo)? = deviceInfoStub // allows user to set `null` manually.
/**
* 使用随机设备信息.
*
@ -198,6 +265,34 @@ public open class BotConfiguration { // open for Java
deviceInfo = getFileBasedDeviceInfoSupplier { workingDir.resolve(filepath) }
}
///////////////////////////////////////////////////////////////////////////
// Logging
///////////////////////////////////////////////////////////////////////////
/**
* 日志记录器
*
* - 默认打印到标准输出, 通过 [MiraiLogger.create]
* - 忽略所有日志: [noBotLog]
* - 重定向到一个目录: `networkLoggerSupplier = { DirectoryLogger("Net ${it.id}") }`
* - 重定向到一个文件: `networkLoggerSupplier = { SingleFileLogger("Net ${it.id}") }`
*
* @see MiraiLogger
*/
public var botLoggerSupplier: ((Bot) -> MiraiLogger) = { MiraiLogger.create("Bot ${it.id}") }
/**
* 网络层日志构造器
*
* - 默认打印到标准输出, 通过 [MiraiLogger.create]
* - 忽略所有日志: [noNetworkLog]
* - 重定向到一个目录: `networkLoggerSupplier = { DirectoryLogger("Net ${it.id}") }`
* - 重定向到一个文件: `networkLoggerSupplier = { SingleFileLogger("Net ${it.id}") }`
*
* @see MiraiLogger
*/
public var networkLoggerSupplier: ((Bot) -> MiraiLogger) = { MiraiLogger.create("Net ${it.id}") }
/**
* 重定向 [网络日志][networkLoggerSupplier] 到指定目录. 若目录不存在将会自动创建 ([File.mkdirs])
@ -265,33 +360,6 @@ public open class BotConfiguration { // open for Java
botLoggerSupplier = { DirectoryLogger(identity(it), workingDir.resolve(dir), retain) }
}
public enum class MiraiProtocol {
/**
* Android 手机. 所有功能都支持.
*/
ANDROID_PHONE,
/**
* Android 平板.
*
* 注意: 不支持戳一戳事件解析
*/
ANDROID_PAD,
/**
* Android 手表.
*/
ANDROID_WATCH,
}
public companion object {
/** 默认的配置实例. 可以进行修改 */
@JvmStatic
public val Default: BotConfiguration = BotConfiguration()
}
/**
* 不显示网络日志. 不推荐.
* @see networkLoggerSupplier 更多日志处理方式
@ -310,86 +378,127 @@ public open class BotConfiguration { // open for Java
botLoggerSupplier = { _ -> SilentLogger }
}
///////////////////////////////////////////////////////////////////////////
// Cache
//////////////////////////////////////////////////////////////////////////
/**
* 使用当前协程的 [coroutineContext] 作为 [parentCoroutineContext].
*
* Bot 将会使用一个 [SupervisorJob] 覆盖 [coroutineContext] 当前协程的 [Job], 并使用当前协程的 [Job] 作为父 [Job]
*
* 用例:
* ```
* coroutineScope {
* val bot = Bot(...) {
* inheritCoroutineContext()
* }
* bot.login()
* } // coroutineScope 会等待 Bot 退出
* ```
*
*
* **注意**: `bot.cancel` 时将会让父 [Job] 也被 cancel.
* ```
* coroutineScope { // this: CoroutineScope
* launch {
* while(isActive) {
* delay(500)
* println("I'm alive")
* }
* }
*
* val bot = Bot(...) {
* inheritCoroutineContext() // 使用 `coroutineScope` 的 Job 作为父 Job
* }
* bot.login()
* bot.cancel() // 取消了整个 `coroutineScope`, 因此上文不断打印 `"I'm alive"` 的协程也会被取消.
* }
* ```
*
* 因此, 此函数尤为适合在 `suspend fun main()` 中使用, 它能阻止主线程退出:
* ```
* suspend fun main() {
* val bot = Bot() {
* inheritCoroutineContext()
* }
* bot.subscribe { ... }
*
* // 主线程不会退出, 直到 Bot 离线.
* }
* ```
*
* 简言之,
* - 若想让 [Bot] 作为 '守护进程' 运行, 则无需调用 [inheritCoroutineContext].
* - 若想让 [Bot] 依赖于当前协程, 让当前协程等待 [Bot] 运行, 则使用 [inheritCoroutineContext]
*
* @see parentCoroutineContext
* `null` 时启用好友列表缓存, 加快初始化速度. 在启用后将会在下载好友列表后保存到文件, 并在修改时自动保存.
* @since 2.4
* @see disableFriendListCache
*/
public var friendListCache: FriendListCache? = FriendListCache()
/**
* 好友列表缓存设置.
* @since 2.4
* @see friendListCache
*/
public class FriendListCache @JvmOverloads constructor(
/**
* 缓存文件位置, 相对于 [workingDir] 的路径.
*
* 注意: 保存的文件仅供内部使用, 将来可能会变化.
*/
public val cacheFile: File = File("cache/friends.json"),
/**
* 在有好友列表修改时自动保存间隔
*/
public val saveIntervalMillis: Long = 60_000,
)
/**
* 禁用好友列表缓存.
* @since 2.4
*/
@JvmSynthetic
@ConfigurationDsl
public suspend inline fun inheritCoroutineContext() {
parentCoroutineContext = coroutineContext
public fun disableFriendListCache() {
friendListCache = null
}
/**
* `null` 时启用群成员列表缓存, 加快初始化速度. 在启用后将会在下载群成员列表后保存到文件, 并在修改时自动保存.
* @since 2.4
* @see disableGroupMemberListCache
*/
public var groupMemberListCache: GroupMemberListCache? = GroupMemberListCache()
/**
* 群成员列表缓存设置.
* @since 2.4
* @see groupMemberListCache
*/
public class GroupMemberListCache @JvmOverloads constructor(
/**
* 缓存目录位置, 相对于 [workingDir] 的路径.
*
* 注意: 保存的文件仅供内部使用, 将来可能会变化.
*/
public val cacheDir: File = File("cache/groups"),
/**
* 在有成员列表修改时自动保存间隔
*/
public val saveIntervalMillis: Long = 60_000,
)
/**
* 禁用群成员列表缓存.
* @since 2.4
*/
@ConfigurationDsl
public fun disableGroupMemberListCache() {
groupMemberListCache = null
}
/**
* 禁用好友列表, 群成员列表, 陌生人列表的缓存.
* @since 2.4
*/
@ConfigurationDsl
public fun disableContactCaches() {
disableFriendListCache()
disableGroupMemberListCache()
}
///////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////
public fun copy(): BotConfiguration {
return BotConfiguration().also { new ->
new.botLoggerSupplier = botLoggerSupplier
new.networkLoggerSupplier = networkLoggerSupplier
new.deviceInfo = deviceInfo
// To structural order
new.workingDir = workingDir
new.json = json
new.parentCoroutineContext = parentCoroutineContext
new.heartbeatPeriodMillis = heartbeatPeriodMillis
new.heartbeatTimeoutMillis = heartbeatTimeoutMillis
new.firstReconnectDelayMillis = firstReconnectDelayMillis
new.reconnectPeriodMillis = reconnectPeriodMillis
new.reconnectionRetryTimes = reconnectionRetryTimes
new.autoReconnectOnForceOffline = autoReconnectOnForceOffline
new.loginSolver = loginSolver
new.protocol = protocol
new.highwayUploadCoroutineCount = highwayUploadCoroutineCount
new.deviceInfo = deviceInfo
new.botLoggerSupplier = botLoggerSupplier
new.networkLoggerSupplier = networkLoggerSupplier
new.friendListCache = friendListCache
new.groupMemberListCache = groupMemberListCache
}
}
/** 标注一个配置 DSL 函数 */
@Target(AnnotationTarget.FUNCTION)
@DslMarker
public annotation class ConfigurationDsl
public companion object {
/** 默认的配置实例. 可以进行修改 */
@JvmStatic
public val Default: BotConfiguration = BotConfiguration()
}
}
/**

View File

@ -16,6 +16,7 @@ package net.mamoe.mirai.utils
import io.ktor.utils.io.charsets.*
import kotlinx.io.core.*
import java.io.File
import kotlin.text.Charsets
@ -124,3 +125,10 @@ public inline fun Input.readString(length: UShort, charset: Charset = Charsets.U
public inline fun Input.readString(length: Byte, charset: Charset = Charsets.UTF_8): String =
String(this.readBytes(length.toInt()), charset = charset)
public fun File.createFileIfNotExists() {
if (!this.exists()) {
this.parentFile.mkdirs()
this.createNewFile()
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.utils
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.StringFormat
import java.io.File
public fun <T> File.loadAs(
serializer: DeserializationStrategy<T>,
stringFormat: StringFormat,
): T? {
if (!this.exists() || this.length() == 0L) {
return null
}
return stringFormat.decodeFromString(serializer, this.readText())
}

View File

@ -9,6 +9,7 @@
package net.mamoe.mirai.internal
import contact.StrangerImpl
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.features.*
@ -19,12 +20,15 @@ import kotlinx.coroutines.withContext
import kotlinx.io.core.discardExact
import kotlinx.io.core.readBytes
import kotlinx.serialization.json.*
import net.mamoe.kjbb.JvmBlockingBridge
import net.mamoe.mirai.*
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.*
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.network.highway.*
import net.mamoe.mirai.internal.network.protocol.data.jce.SvcDevLoginInfo

View File

@ -19,24 +19,24 @@ import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.*
import net.mamoe.mirai.internal.contact.OtherClientImpl
import net.mamoe.mirai.internal.contact.StrangerInfoImpl
import net.mamoe.mirai.internal.contact.checkIsGroupImpl
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.contact.uin
import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidBotNetworkHandler
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.*
import net.mamoe.mirai.internal.network.handler.QQAndroidBotNetworkHandler
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketWithRespType
import net.mamoe.mirai.internal.network.protocol.packet.chat.*
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
import net.mamoe.mirai.internal.network.useNextServers
import net.mamoe.mirai.internal.utils.ScheduledJob
import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.network.LoginFailedException
import net.mamoe.mirai.utils.*
import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
import net.mamoe.mirai.internal.network.protocol.data.jce.FriendInfo as JceFriendInfo
import kotlin.time.milliseconds
internal fun Bot.asQQAndroidBot(): QQAndroidBot {
contract {
@ -58,7 +58,7 @@ internal class QQAndroidBot constructor(
configuration: BotConfiguration
) : AbstractBot<QQAndroidBotNetworkHandler>(configuration, account.id) {
var client: QQAndroidClient = initClient()
private set
private set
fun initClient(): QQAndroidClient {
client = QQAndroidClient(
@ -77,15 +77,45 @@ internal class QQAndroidBot constructor(
override val friends: ContactList<Friend> = ContactList()
override lateinit var nick: String
val friendListCache: FriendListCache? by lazy {
configuration.friendListCache?.cacheFile?.let { cacheFile ->
val ret = configuration.workingDir.resolve(cacheFile).loadAs(FriendListCache.serializer(), JsonForCache) ?: FriendListCache()
internal var selfInfo: JceFriendInfo? = null
get() = field ?: error("selfInfo is not yet initialized")
set(it) {
checkNotNull(it)
field = it
nick = it.nick
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
bot.eventChannel.parentScope(this@QQAndroidBot)
.subscribeAlways<net.mamoe.mirai.event.events.FriendInfoChangeEvent> {
friendListSaver?.notice()
}
ret
}
}
val groupMemberListCaches: GroupMemberListCaches? by lazy {
if (configuration.groupMemberListCache!= null) {
GroupMemberListCaches(this)
} else null
}
private val friendListSaver by lazy {
configuration.friendListCache?.let { friendListCache: BotConfiguration.FriendListCache ->
ScheduledJob(coroutineContext, friendListCache.saveIntervalMillis.milliseconds) {
runBIO { saveFriendCache() }
}
}
}
fun saveFriendCache() {
val friendListCache = friendListCache
if (friendListCache != null) {
configuration.friendListCache?.cacheFile?.let { configuration.workingDir.resolve(it) }?.run {
createFileIfNotExists()
writeText(JsonForCache.encodeToString(FriendListCache.serializer(), friendListCache))
bot.network.logger.info { "Saved ${friendListCache.list.size} friends to local cache." }
}
}
}
override lateinit var nick: String
override val asFriend: Friend by lazy {
@OptIn(LowLevelApi::class)
@ -107,7 +137,7 @@ internal class QQAndroidBot constructor(
override suspend fun sendLogout() {
network.run {
StatSvc.Register.offline(client). sendWithoutExpect()
StatSvc.Register.offline(client).sendWithoutExpect()
}
}

View File

@ -12,6 +12,7 @@ package net.mamoe.mirai.internal.contact
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.utils.cast
import net.mamoe.mirai.utils.getValue
import net.mamoe.mirai.utils.unsafeWeakRef

View File

@ -40,9 +40,6 @@ import kotlin.coroutines.CoroutineContext
internal val User.info: UserInfo? get() = this.castOrNull<AbstractUser>()?.info
internal open class UserInfoImpl(override val uin: Long, override val nick: String, override val remark: String = "") :
UserInfo
internal abstract class AbstractUser(
bot: Bot,
coroutineContext: CoroutineContext,

View File

@ -23,10 +23,10 @@ import kotlinx.atomicfu.atomic
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.contact.Friend
import net.mamoe.mirai.data.FriendInfo
import net.mamoe.mirai.data.FriendInfoImpl
import net.mamoe.mirai.event.events.FriendMessagePostSendEvent
import net.mamoe.mirai.event.events.FriendMessagePreSendEvent
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.utils.C2CPkgMsgParsingCache
import net.mamoe.mirai.message.MessageReceipt
@ -42,15 +42,6 @@ internal fun net.mamoe.mirai.internal.network.protocol.data.jce.FriendInfo.toMir
remark
)
@OptIn(ExperimentalContracts::class)
internal inline fun FriendInfo.checkIsInfoImpl(): FriendInfoImpl {
contract {
returns() implies (this@checkIsInfoImpl is FriendInfoImpl)
}
check(this is FriendInfoImpl) { "A FriendInfo instance is not instance of checkIsInfoImpl. Your instance: ${this::class.qualifiedName}" }
return this
}
@OptIn(ExperimentalContracts::class)
internal inline fun Friend.checkIsFriendImpl(): FriendImpl {
contract {

View File

@ -20,9 +20,10 @@ import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.message.*
import net.mamoe.mirai.internal.network.BdhSession
import net.mamoe.mirai.internal.network.QQAndroidBotNetworkHandler
import net.mamoe.mirai.internal.network.handler.QQAndroidBotNetworkHandler
import net.mamoe.mirai.internal.network.highway.*
import net.mamoe.mirai.internal.network.highway.ResourceKind.GROUP_IMAGE
import net.mamoe.mirai.internal.network.highway.ResourceKind.GROUP_VOICE

View File

@ -9,6 +9,7 @@
package net.mamoe.mirai.internal.contact
import contact.StrangerImpl
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.event.nextEventOrNull
import net.mamoe.mirai.internal.MiraiImpl

View File

@ -15,17 +15,19 @@
"INVISIBLE_REFERENCE"
)
package net.mamoe.mirai.internal.contact
package contact
import kotlinx.atomicfu.AtomicInt
import kotlinx.atomicfu.atomic
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.FriendInfoImpl
import net.mamoe.mirai.data.StrangerInfo
import net.mamoe.mirai.event.events.StrangerMessagePostSendEvent
import net.mamoe.mirai.event.events.StrangerMessagePreSendEvent
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.AbstractUser
import net.mamoe.mirai.internal.contact.StrangerSendMessageHandler
import net.mamoe.mirai.internal.contact.sendMessageImpl
import net.mamoe.mirai.internal.message.OnlineMessageSourceToStrangerImpl
import net.mamoe.mirai.internal.network.protocol.packet.list.StrangerList
import net.mamoe.mirai.message.MessageReceipt
@ -36,20 +38,6 @@ import kotlin.contracts.contract
import kotlin.coroutines.CoroutineContext
internal class StrangerInfoImpl(
override val uin: Long, override val nick: String, override val fromGroup: Long = 0,
override val remark: String = ""
) : StrangerInfo
@OptIn(ExperimentalContracts::class)
internal inline fun StrangerInfo.checkIsInfoImpl(): FriendInfoImpl {
contract {
returns() implies (this@checkIsInfoImpl is StrangerInfoImpl)
}
check(this is FriendInfoImpl) { "A StrangerInfo instance is not instance of StrangerInfoImpl. Your instance: ${this::class.qualifiedName}" }
return this
}
@OptIn(ExperimentalContracts::class)
internal inline fun Stranger.checkIsImpl(): StrangerImpl {
contract {

View File

@ -0,0 +1,21 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.contact.info
import kotlinx.serialization.Serializable
import net.mamoe.mirai.data.FriendInfo
// since 2.4, for serialization
@Serializable
internal data class FriendInfoImpl(
override val uin: Long,
override var nick: String,
override var remark: String,
) : FriendInfo

View File

@ -0,0 +1,44 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.contact.info
import kotlinx.serialization.Serializable
import net.mamoe.mirai.data.GroupInfo
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopNum
@Serializable
internal data class GroupInfoImpl(
override val uin: Long,
override val owner: Long,
override val groupCode: Long,
override val memo: String,
override val name: String,
override val allowMemberInvite: Boolean,
override val allowAnonymousChat: Boolean,
override val autoApprove: Boolean,
override val confessTalk: Boolean,
override val muteAll: Boolean,
override val botMuteTimestamp: Int,
) : GroupInfo, Packet, Packet.NoLog {
constructor(stTroopNum: StTroopNum) : this(
uin = stTroopNum.groupUin,
owner = stTroopNum.dwGroupOwnerUin,
groupCode = stTroopNum.groupCode,
memo = stTroopNum.groupMemo,
name = stTroopNum.groupName,
allowMemberInvite = stTroopNum.dwGroupFlagExt?.and(0x000000c0) != 0L,
allowAnonymousChat = stTroopNum.dwGroupFlagExt?.and(0x40000000) == 0L,
autoApprove = stTroopNum.dwGroupFlagExt3?.and(0x00100000) == 0L,
confessTalk = stTroopNum.dwGroupFlagExt3?.and(0x00002000) == 0L,
muteAll = stTroopNum.dwShutUpTimestamp != 0L,
botMuteTimestamp = stTroopNum.dwMyShutUpTimestamp?.toInt() ?: 0,
)
}

View File

@ -7,15 +7,17 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.contact
package net.mamoe.mirai.internal.contact.info
import kotlinx.serialization.Serializable
import net.mamoe.mirai.contact.MemberPermission
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopMemberInfo
import net.mamoe.mirai.utils.currentTimeSeconds
internal class MemberInfoImpl(
@Serializable
internal data class MemberInfoImpl(
override val uin: Long,
override var nick: String,
override var permission: MemberPermission,
@ -27,7 +29,7 @@ internal class MemberInfoImpl(
override val joinTimestamp: Int = currentTimeSeconds().toInt(),
override var lastSpeakTimestamp: Int = 0,
override val isOfficialBot: Boolean = false
) : MemberInfo, UserInfoImpl(uin, nick, remark) {
) : MemberInfo {
constructor(
client: QQAndroidClient,
jceInfo: StTroopMemberInfo,

View File

@ -0,0 +1,23 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.contact.info
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import net.mamoe.mirai.data.StrangerInfo
@SerialName("StrangerInfo")
@Serializable
internal class StrangerInfoImpl(
override val uin: Long,
override val nick: String,
override val fromGroup: Long = 0,
override val remark: String = ""
) : StrangerInfo

View File

@ -18,6 +18,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import net.mamoe.mirai.Bot
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.utils.MiraiInternalApi
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.WeakRefProperty
@ -46,7 +47,7 @@ internal abstract class BotNetworkHandler : CoroutineScope {
* 所属 [Bot]. 为弱引用
*/
@WeakRefProperty
abstract val bot: Bot
abstract val bot: QQAndroidBot
/**
* 监管 child [Job]s

View File

@ -0,0 +1,126 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopNum
import net.mamoe.mirai.internal.utils.ScheduledJob
import net.mamoe.mirai.utils.createFileIfNotExists
import net.mamoe.mirai.utils.info
import net.mamoe.mirai.utils.runBIO
import java.io.File
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue
import kotlin.time.milliseconds
internal val JsonForCache = Json {
encodeDefaults = true
ignoreUnknownKeys = true
isLenient = true
}
@Serializable
internal data class FriendListCache(
var friendListSeq: Long = 0,
/**
* 实际上是个序列号, 不是时间
*/
var timeStamp: Long = 0,
var list: List<FriendInfoImpl> = emptyList(),
)
@Serializable
internal data class GroupMemberListCache(
var troopMemberNumSeq: Long,
var list: List<MemberInfoImpl> = emptyList(),
)
internal fun GroupMemberListCache.isValid(stTroopNum: StTroopNum): Boolean {
return this.list.size == stTroopNum.dwMemberNum?.toInt() && this.troopMemberNumSeq == stTroopNum.dwMemberNumSeq
}
internal class GroupMemberListCaches(
private val bot: QQAndroidBot,
) {
init {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
bot.eventChannel.parentScope(bot)
.subscribeAlways<net.mamoe.mirai.event.events.BaseGroupMemberInfoChangeEvent> {
groupListSaver.notice()
}
}
private val changedGroups: MutableCollection<Long> = ConcurrentLinkedQueue()
private val groupListSaver by lazy {
ScheduledJob(bot.coroutineContext, bot.configuration.groupMemberListCache!!.saveIntervalMillis.milliseconds) {
runBIO { saveGroupCaches() }
}
}
fun reportChanged(groupCode: Long) {
changedGroups.add(groupCode)
groupListSaver.notice()
}
private fun takeCurrentChangedGroups(): Map<Long, GroupMemberListCache> {
val ret = HashMap<Long, GroupMemberListCache>()
changedGroups.removeIf {
ret[it] = get(it)
true
}
return ret
}
private val cacheDir by lazy {
bot.configuration.groupMemberListCache!!.cacheDir.let { bot.configuration.workingDir.resolve(it) }
}
private fun resolveCacheFile(groupCode: Long): File {
cacheDir.mkdirs()
return cacheDir.resolve("$groupCode.json")
}
fun saveGroupCaches() {
val currentChanged = takeCurrentChangedGroups()
if (currentChanged.isNotEmpty()) {
for ((id, cache) in currentChanged) {
val file = resolveCacheFile(id)
file.createFileIfNotExists()
file.writeText(JsonForCache.encodeToString(GroupMemberListCache.serializer(), cache))
}
bot.network.logger.info { "Saved ${currentChanged.size} groups to local cache." }
}
}
val map: MutableMap<Long, GroupMemberListCache> = ConcurrentHashMap()
fun retainAll(list: Collection<Long>) {
this.map.keys.retainAll(list)
}
operator fun get(id: Long): GroupMemberListCache {
return map.getOrPut(id) {
val file = resolveCacheFile(id)
if (file.exists() && file.isFile) {
val text = file.readText()
if (text.isNotBlank()) {
return JsonForCache.decodeFromString(GroupMemberListCache.serializer(), text)
}
}
GroupMemberListCache(0, emptyList())
}
}
}

View File

@ -0,0 +1,232 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.network
import contact.StrangerImpl
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.data.FriendInfo
import net.mamoe.mirai.data.MemberInfo
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.FriendImpl
import net.mamoe.mirai.internal.contact.GroupImpl
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.GroupInfoImpl
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.contact.toMiraiFriendInfo
import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopNum
import net.mamoe.mirai.internal.network.protocol.data.jce.SvcRespRegister
import net.mamoe.mirai.internal.network.protocol.data.jce.isValid
import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.network.protocol.packet.list.StrangerList
import net.mamoe.mirai.utils.info
import net.mamoe.mirai.utils.retryCatching
import net.mamoe.mirai.utils.verbose
internal interface ContactUpdater {
suspend fun loadAll(registerResp: SvcRespRegister)
fun closeAllContacts(e: CancellationException)
}
internal class ContactUpdaterImpl(
val bot: QQAndroidBot,
) : ContactUpdater {
@Synchronized
override suspend fun loadAll(registerResp: SvcRespRegister) {
coroutineScope {
launch { reloadFriendList(registerResp) }
launch { reloadGroupList() }
launch { reloadStrangerList() }
}
}
@Synchronized
override fun closeAllContacts(e: CancellationException) {
if (!initFriendOk) {
bot.friends.delegate.removeAll { it.cancel(e); true }
}
if (!initGroupOk) {
bot.groups.delegate.removeAll { it.cancel(e); true }
}
if (!initStrangerOk) {
bot.strangers.delegate.removeAll { it.cancel(e); true }
}
}
@Volatile
private var initFriendOk = false
@Volatile
private var initGroupOk = false
@Volatile
private var initStrangerOk = false
/**
* Don't use concurrently
*/
private suspend fun reloadFriendList(registerResp: SvcRespRegister) = bot.network.run {
if (initFriendOk) {
return
}
val friendListCache = bot.friendListCache
fun updateCacheSeq(list: List<FriendInfoImpl>) {
bot.friendListCache?.apply {
friendListSeq = registerResp.iLargeSeq
timeStamp = registerResp.timeStamp
this.list = list
bot.saveFriendCache()
}
}
suspend fun refreshFriendList(): List<FriendInfoImpl> {
logger.info { "Start loading friend list..." }
val friendInfos = mutableListOf<FriendInfoImpl>()
var count = 0
var total: Short
while (true) {
val data = FriendList.GetFriendGroupList(
bot.client, count, 150, 0, 0
).sendAndExpect<FriendList.GetFriendGroupList.Response>(timeoutMillis = 5000, retry = 2)
total = data.totalFriendCount
for (jceInfo in data.friendList) {
friendInfos.add(jceInfo.toMiraiFriendInfo())
}
count += data.friendList.size
logger.verbose { "Loading friend list: ${count}/${total}" }
if (count >= total) break
}
logger.info { "Successfully loaded friend list: $count in total" }
return friendInfos
}
val list = if (friendListCache?.isValid(registerResp) == true) {
val list = friendListCache.list
bot.network.logger.info { "Loaded ${list.size} friends from local cache." }
list
} else {
refreshFriendList().also {
updateCacheSeq(it)
}
}
for (friendInfoImpl in list) {
addFriendToBot(friendInfoImpl)
}
initFriendOk = true
}
private fun addFriendToBot(it: FriendInfo) =
bot.friends.delegate.add(FriendImpl(bot, bot.coroutineContext, it))
private suspend fun addGroupToBot(stTroopNum: StTroopNum) = stTroopNum.run {
suspend fun refreshGroupMemberList(): Sequence<MemberInfo> {
return Mirai.getRawGroupMemberList(
bot,
groupUin,
groupCode,
dwGroupOwnerUin
)
}
val cache = bot.groupMemberListCaches?.get(groupCode)
val members = if (cache != null) {
if (cache.isValid(stTroopNum)) {
cache.list.asSequence().also {
bot.network.logger.info { "Loaded ${cache.list.size} members from local cache for group ${groupName} (${groupCode})" }
}
} else refreshGroupMemberList().also { sequence ->
cache.troopMemberNumSeq = dwMemberNumSeq ?: 0
cache.list = sequence.mapTo(ArrayList()) { it as MemberInfoImpl }
bot.groupMemberListCaches!!.reportChanged(groupCode)
}
} else {
refreshGroupMemberList()
}
bot.groups.delegate.add(
GroupImpl(
bot = bot,
coroutineContext = bot.coroutineContext,
id = groupCode,
groupInfo = GroupInfoImpl(stTroopNum),
members = members
)
)
}
private suspend fun reloadStrangerList() = bot.network.run {
if (initStrangerOk) {
return
}
var currentCount = 0
logger.info { "Start loading stranger list..." }
val response = StrangerList.GetStrangerList(bot.client)
.sendAndExpect<StrangerList.GetStrangerList.Response>(timeoutMillis = 5000, retry = 2)
if (response.result == 0) {
response.strangerList.forEach {
// atomic
bot.strangers.delegate.add(
StrangerImpl(bot, bot.coroutineContext, StrangerInfoImpl(it.uin, it.nick.decodeToString()))
).also { currentCount++ }
}
}
logger.info { "Successfully loaded stranger list: $currentCount in total" }
initStrangerOk = true
}
private suspend fun reloadGroupList() = bot.network.run {
if (initGroupOk) {
return
}
TroopManagement.GetTroopConfig(bot.client).sendAndExpect<TroopManagement.GetTroopConfig.Response>()
logger.info { "Start loading group list..." }
val troopListData = FriendList.GetTroopListSimplify(bot.client)
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 5)
val semaphore = Semaphore(30)
coroutineScope {
troopListData.groups.forEach { group ->
launch {
semaphore.withPermit {
retryCatching(5) { addGroupToBot(group) }.getOrThrow()
}
}
}
}
logger.info { "Successfully loaded group list: ${troopListData.groups.size} in total." }
bot.groupMemberListCaches?.saveGroupCaches()
initGroupOk = true
}
}

View File

@ -7,17 +7,14 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.internal.network
package net.mamoe.mirai.internal.network.handler
import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.sync.withPermit
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.buildPacket
import kotlinx.io.core.readBytes
@ -29,15 +26,11 @@ import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.createOtherClient
import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopNum
import net.mamoe.mirai.internal.network.*
import net.mamoe.mirai.internal.network.protocol.data.proto.MsgSvc
import net.mamoe.mirai.internal.network.protocol.packet.*
import net.mamoe.mirai.internal.network.protocol.packet.KnownPacketFactories.PacketFactoryIllegalStateException
import net.mamoe.mirai.internal.network.protocol.packet.chat.GroupInfoImpl
import net.mamoe.mirai.internal.network.protocol.packet.chat.TroopManagement
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbGetMsg
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.network.protocol.packet.list.StrangerList
import net.mamoe.mirai.internal.network.protocol.packet.login.ConfigPushSvc
import net.mamoe.mirai.internal.network.protocol.packet.login.Heartbeat
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
@ -272,13 +265,8 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
// println("d2key=${bot.client.wLoginSigInfo.d2Key.toUHexString()}")
registerClientOnline()
startHeartbeatJobOrKill()
bot.otherClientsLock.withLock {
updateOtherClientsList()
}
launch {
while (isActive) {
bot.client.wLoginSigInfo.sKey.run {
@ -299,17 +287,17 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
WtLogin15(bot.client).sendAndExpect()
}
private suspend fun registerClientOnline() {
private suspend fun registerClientOnline(): StatSvc.Register.Response {
// object : OutgoingPacketFactory<Packet?>("push.proxyUnRegister") {
// override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Packet? {
// return null
// }
// }.buildOutgoingUniPacket(bot.client) {}.sendWithoutExpect()
kotlin.runCatching {
StatSvc.Register.offline(bot.client).sendAndExpect()
}.getOrElse { logger.warning(it) }
// kotlin.runCatching {
// StatSvc.Register.offline(bot.client).sendAndExpect()
// }.getOrElse { logger.warning(it) }
StatSvc.Register.online(bot.client).sendAndExpect()
return StatSvc.Register.online(bot.client).sendAndExpect()
}
private suspend fun updateOtherClientsList() {
@ -333,177 +321,67 @@ internal class QQAndroidBotNetworkHandler(coroutineContext: CoroutineContext, bo
internal var pendingIncomingPackets: ConcurrentLinkedQueue<KnownPacketFactories.IncomingPacket<*>>? =
ConcurrentLinkedQueue()
private var initFriendOk = false
private var initGroupOk = false
private var initStrangerOk = false
/**
* Don't use concurrently
*/
suspend fun reloadFriendList() {
if (initFriendOk) {
return
}
logger.info { "Start loading friend list..." }
var currentFriendCount = 0
var totalFriendCount: Short
while (true) {
val data = FriendList.GetFriendGroupList(
bot.client, currentFriendCount, 150, 0, 0
).sendAndExpect<FriendList.GetFriendGroupList.Response>(timeoutMillis = 5000, retry = 2)
// self info
data.selfInfo?.run {
bot.selfInfo = this
// bot.remark = remark ?: ""
// bot.sex = sex
}
totalFriendCount = data.totalFriendCount
data.friendList.forEach {
// atomic
bot.friends.delegate.add(
FriendImpl(bot, bot.coroutineContext, it.toMiraiFriendInfo())
).also { currentFriendCount++ }
}
logger.verbose { "Loading friend list: ${currentFriendCount}/${totalFriendCount}" }
if (currentFriendCount >= totalFriendCount) {
break
}
// delay(200)
}
logger.info { "Successfully loaded friend list: $currentFriendCount in total" }
initFriendOk = true
}
suspend fun StTroopNum.reloadGroup() {
bot.groups.delegate.add(
GroupImpl(
bot = bot,
coroutineContext = bot.coroutineContext,
id = groupCode,
groupInfo = GroupInfoImpl(this),
members = Mirai.getRawGroupMemberList(
bot,
groupUin,
groupCode,
dwGroupOwnerUin
)
)
)
}
suspend fun reloadStrangerList() {
if (initStrangerOk) {
return
}
var currentCount = 0
logger.info { "Start loading stranger list..." }
val response = StrangerList.GetStrangerList(bot.client)
.sendAndExpect<StrangerList.GetStrangerList.Response>(timeoutMillis = 5000, retry = 2)
if (response.result == 0) {
response.strangerList.forEach {
// atomic
bot.strangers.delegate.add(
StrangerImpl(bot, bot.coroutineContext, StrangerInfoImpl(it.uin, it.nick.decodeToString()))
).also { currentCount++ }
}
}
logger.info { "Successfully loaded stranger list: $currentCount in total" }
initStrangerOk = true
}
suspend fun reloadGroupList() {
if (initGroupOk) {
return
}
logger.info { "Start syncing group config..." }
TroopManagement.GetTroopConfig(bot.client).sendAndExpect<TroopManagement.GetTroopConfig.Response>()
logger.info { "Successfully synced group config." }
logger.info { "Start loading group list..." }
val troopListData = FriendList.GetTroopListSimplify(bot.client)
.sendAndExpect<FriendList.GetTroopListSimplify.Response>(retry = 5)
val semaphore = Semaphore(30)
coroutineScope {
troopListData.groups.forEach { group ->
launch {
semaphore.withPermit {
retryCatching(5) { group.reloadGroup() }.getOrThrow()
}
}
}
}
logger.info { "Successfully loaded group list: ${troopListData.groups.size} in total." }
initGroupOk = true
}
private val contactUpdater: ContactUpdater by lazy { ContactUpdaterImpl(bot) }
override suspend fun init(): Unit = coroutineScope {
check(bot.isActive) { "bot is dead therefore network can't init." }
check(this@QQAndroidBotNetworkHandler.isActive) { "network is dead therefore can't init." }
CancellationException("re-init").let { reInitCancellationException ->
if (!initFriendOk) {
bot.friends.delegate.removeAll { it.cancel(reInitCancellationException); true }
}
if (!initGroupOk) {
bot.groups.delegate.removeAll { it.cancel(reInitCancellationException); true }
}
if (!initStrangerOk) {
bot.strangers.delegate.removeAll { it.cancel(reInitCancellationException); true }
}
}
contactUpdater.closeAllContacts(CancellationException("re-init"))
if (!pendingEnabled) {
pendingIncomingPackets = ConcurrentLinkedQueue()
_pendingEnabled.value = true
}
coroutineScope {
launch { reloadFriendList() }
launch { reloadGroupList() }
launch { reloadStrangerList() }
val registerResp = registerClientOnline()
this@QQAndroidBotNetworkHandler.launch(CoroutineName("Awaiting ConfigPushSvc.PushReq"), block= ConfigPushSyncer())
launch {
syncMessageSvc()
}
this@QQAndroidBotNetworkHandler.launch(CoroutineName("Awaiting ConfigPushSvc.PushReq")) {
logger.info { "Awaiting ConfigPushSvc.PushReq." }
when (val resp: ConfigPushSvc.PushReq.PushReqResponse? = nextEventOrNull(20_000)) {
null -> {
kotlin.runCatching { bot.client.bdhSession.completeExceptionally(TimeoutCancellationException("Timeout waiting for ConfigPushSvc.PushReq")) }
logger.warning { "Missing ConfigPushSvc.PushReq. File uploading may be affected." }
}
is ConfigPushSvc.PushReq.PushReqResponse.Success -> {
logger.info { "ConfigPushSvc.PushReq: Success." }
}
is ConfigPushSvc.PushReq.PushReqResponse.ChangeServer -> {
bot.logger.info { "Server requires reconnect." }
bot.logger.info { "Server list: ${resp.serverList.joinToString()}." }
if (resp.serverList.isNotEmpty()) {
bot.serverList.clear()
resp.serverList.shuffled().forEach {
bot.serverList.add(it.host to it.port)
}
}
bot.launch { BotOfflineEvent.RequireReconnect(bot).broadcast() }
return@launch
}
launch {
bot.otherClientsLock.withLock {
updateOtherClientsList()
}
}
syncMessageSvc()
contactUpdater.loadAll(registerResp.origin)
bot.firstLoginSucceed = true
postInitActions()
}
@Suppress("FunctionName")
private fun BotNetworkHandler.ConfigPushSyncer(): suspend CoroutineScope.() -> Unit = launch@{
logger.info { "Awaiting ConfigPushSvc.PushReq." }
when (val resp: ConfigPushSvc.PushReq.PushReqResponse? = nextEventOrNull(20_000)) {
null -> {
kotlin.runCatching { bot.client.bdhSession.completeExceptionally(CancellationException("Timeout waiting for ConfigPushSvc.PushReq")) }
logger.warning { "Missing ConfigPushSvc.PushReq. File uploading may be affected." }
}
is ConfigPushSvc.PushReq.PushReqResponse.Success -> {
logger.info { "ConfigPushSvc.PushReq: Success." }
}
is ConfigPushSvc.PushReq.PushReqResponse.ChangeServer -> {
bot.logger.info { "Server requires reconnect." }
bot.logger.info { "Server list: ${resp.serverList.joinToString()}." }
if (resp.serverList.isNotEmpty()) {
bot.serverList.clear()
resp.serverList.shuffled().forEach {
bot.serverList.add(it.host to it.port)
}
}
bot.launch { BotOfflineEvent.RequireReconnect(bot).broadcast() }
return@launch
}
}
}
override suspend fun postInitActions() {
_pendingEnabled.value = false
pendingIncomingPackets?.forEach {

View File

@ -15,41 +15,41 @@ import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
@Serializable
internal class SvcReqRegister(
@TarsId(0) @JvmField val lUin: Long = 0L,
@TarsId(1) @JvmField val lBid: Long = 0L,
@TarsId(2) @JvmField val cConnType: Byte = 0,
@TarsId(3) @JvmField val sOther: String = "",
@TarsId(4) @JvmField val iStatus: Int = 11,
@TarsId(5) @JvmField val bOnlinePush: Byte = 0,
@TarsId(6) @JvmField val bIsOnline: Byte = 0,
@TarsId(7) @JvmField val bIsShowOnline: Byte = 0,
@TarsId(8) @JvmField val bKikPC: Byte = 0,
@TarsId(9) @JvmField val bKikWeak: Byte = 0,
@TarsId(10) @JvmField val timeStamp: Long = 0L,
@TarsId(11) @JvmField val iOSVersion: Long = 0L,
@TarsId(12) @JvmField val cNetType: Byte = 0,
@TarsId(13) @JvmField val sBuildVer: String? = "",
@TarsId(14) @JvmField val bRegType: Byte = 0,
@TarsId(15) @JvmField val vecDevParam: ByteArray? = null,
@TarsId(16) @JvmField val vecGuid: ByteArray? = null,
@TarsId(17) @JvmField val iLocaleID: Int = 2052,
@TarsId(18) @JvmField val bSlientPush: Byte = 0,
@TarsId(19) @JvmField val strDevName: String? = null,
@TarsId(20) @JvmField val strDevType: String? = null,
@TarsId(21) @JvmField val strOSVer: String? = null,
@TarsId(22) @JvmField val bOpenPush: Byte = 1,
@TarsId(23) @JvmField val iLargeSeq: Long = 0L,
@TarsId(24) @JvmField val iLastWatchStartTime: Long = 0L,
@TarsId(26) @JvmField val uOldSSOIp: Long = 0L,
@TarsId(27) @JvmField val uNewSSOIp: Long = 0L,
@TarsId(28) @JvmField val sChannelNo: String? = null,
@TarsId(29) @JvmField val lCpId: Long = 0L,
@TarsId(30) @JvmField val strVendorName: String? = null,
@TarsId(31) @JvmField val strVendorOSName: String? = null,
@TarsId(32) @JvmField val strIOSIdfa: String? = null,
@TarsId(33) @JvmField val bytes_0x769_reqbody: ByteArray? = null,
@TarsId(34) @JvmField val bIsSetStatus: Byte = 0,
@TarsId(35) @JvmField val vecServerBuf: ByteArray? = null,
@TarsId(36) @JvmField val bSetMute: Byte = 0
@TarsId(0) @JvmField var lUin: Long = 0L,
@TarsId(1) @JvmField var lBid: Long = 0L,
@TarsId(2) @JvmField var cConnType: Byte = 0,
@TarsId(3) @JvmField var sOther: String = "",
@TarsId(4) @JvmField var iStatus: Int = 11,
@TarsId(5) @JvmField var bOnlinePush: Byte = 0,
@TarsId(6) @JvmField var bIsOnline: Byte = 0,
@TarsId(7) @JvmField var bIsShowOnline: Byte = 0,
@TarsId(8) @JvmField var bKikPC: Byte = 0,
@TarsId(9) @JvmField var bKikWeak: Byte = 0,
@TarsId(10) @JvmField var timeStamp: Long = 0L,
@TarsId(11) @JvmField var iOSVersion: Long = 0L,
@TarsId(12) @JvmField var cNetType: Byte = 0,
@TarsId(13) @JvmField var sBuildVer: String? = "",
@TarsId(14) @JvmField var bRegType: Byte = 0,
@TarsId(15) @JvmField var vecDevParam: ByteArray? = null,
@TarsId(16) @JvmField var vecGuid: ByteArray? = null,
@TarsId(17) @JvmField var iLocaleID: Int = 2052,
@TarsId(18) @JvmField var bSlientPush: Byte = 0,
@TarsId(19) @JvmField var strDevName: String? = null,
@TarsId(20) @JvmField var strDevType: String? = null,
@TarsId(21) @JvmField var strOSVer: String? = null,
@TarsId(22) @JvmField var bOpenPush: Byte,
@TarsId(23) @JvmField var iLargeSeq: Long,
@TarsId(24) @JvmField var iLastWatchStartTime: Long = 0L,
@TarsId(26) @JvmField var uOldSSOIp: Long = 0L,
@TarsId(27) @JvmField var uNewSSOIp: Long = 0L,
@TarsId(28) @JvmField var sChannelNo: String? = null,
@TarsId(29) @JvmField var lCpId: Long = 0L,
@TarsId(30) @JvmField var strVendorName: String? = null,
@TarsId(31) @JvmField var strVendorOSName: String? = null,
@TarsId(32) @JvmField var strIOSIdfa: String? = null,
@TarsId(33) @JvmField var bytes_0x769_reqbody: ByteArray? = null,
@TarsId(34) @JvmField var bIsSetStatus: Byte = 0,
@TarsId(35) @JvmField var vecServerBuf: ByteArray? = null,
@TarsId(36) @JvmField var bSetMute: Byte = 0
// @SerialId(25) var vecBindUin: ArrayList<*>? = null // ?? 未知泛型
) : JceStruct

View File

@ -10,6 +10,7 @@
package net.mamoe.mirai.internal.network.protocol.data.jce
import kotlinx.serialization.Serializable
import net.mamoe.mirai.internal.network.FriendListCache
import net.mamoe.mirai.internal.utils.io.JceStruct
import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
@ -30,7 +31,15 @@ internal class SvcRespRegister(
@JvmField @TarsId(11) val iClientPort: Int = 0,
@JvmField @TarsId(12) val iHelloInterval: Int = 300,
@JvmField @TarsId(13) val iLargeSeq: Long = 0L,
/**
* =1 好友列表更新
*/
@JvmField @TarsId(14) val largeSeqUpdate: Byte = 0,
@JvmField @TarsId(15) val bytes_0x769_rspBody: ByteArray? = null,
@JvmField @TarsId(16) val iStatus: Int? = 0
) : JceStruct
internal fun FriendListCache.isValid(svcRespRegister: SvcRespRegister): Boolean {
return svcRespRegister.iLargeSeq == friendListSeq && svcRespRegister.timeStamp == timeStamp
// return this.largeSeqUpdate != 0.toByte()
}

View File

@ -16,7 +16,7 @@ import kotlinx.io.core.buildPacket
import kotlinx.io.core.writeFully
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidBotNetworkHandler
import net.mamoe.mirai.internal.network.handler.QQAndroidBotNetworkHandler
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.utils.io.encryptAndWrite
import net.mamoe.mirai.internal.utils.io.writeHex

View File

@ -13,14 +13,13 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.buildPacket
import kotlinx.io.core.readBytes
import kotlinx.io.core.toByteArray
import net.mamoe.mirai.LowLevelApi
import net.mamoe.mirai.contact.Member
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.info.GroupInfoImpl
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.protocol.data.jce.ModifyGroupCardReq
import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPacket
import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopNum
import net.mamoe.mirai.internal.network.protocol.data.jce.stUinInfo
import net.mamoe.mirai.internal.network.protocol.data.proto.*
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
@ -28,24 +27,6 @@ import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.internal.utils.io.serialization.*
import net.mamoe.mirai.utils.daysToSeconds
import net.mamoe.mirai.data.GroupInfo as MiraiGroupInfo
@OptIn(LowLevelApi::class)
internal class GroupInfoImpl(
private val stTroopNum: StTroopNum
) : MiraiGroupInfo, Packet, Packet.NoLog {
override val uin: Long get() = stTroopNum.groupUin
override val owner: Long get() = stTroopNum.dwGroupOwnerUin
override val groupCode: Long get() = stTroopNum.groupCode
override val memo: String get() = stTroopNum.groupMemo
override val name: String get() = stTroopNum.groupName
override val allowMemberInvite get() = stTroopNum.dwGroupFlagExt?.and(0x000000c0) != 0L
override val allowAnonymousChat get() = stTroopNum.dwGroupFlagExt?.and(0x40000000) == 0L
override val autoApprove get() = stTroopNum.dwGroupFlagExt3?.and(0x00100000) == 0L
override val confessTalk get() = stTroopNum.dwGroupFlagExt3?.and(0x00002000) == 0L
override val muteAll: Boolean get() = stTroopNum.dwShutUpTimestamp != 0L
override val botMuteTimestamp: Int get() = stTroopNum.dwMyShutUpTimestamp?.toInt() ?: 0
}
internal class TroopManagement {

View File

@ -11,6 +11,7 @@
package net.mamoe.mirai.internal.network.protocol.packet.chat.receive
import contact.checkIsImpl
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
@ -29,6 +30,8 @@ import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
import net.mamoe.mirai.internal.message.OnlineMessageSourceFromFriendImpl
import net.mamoe.mirai.internal.message.refine
import net.mamoe.mirai.internal.message.toMessageChainOnline
@ -42,7 +45,7 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.SubMsgType0x7
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory
import net.mamoe.mirai.internal.network.protocol.packet.buildOutgoingUniPacket
import net.mamoe.mirai.internal.network.protocol.packet.chat.GroupInfoImpl
import net.mamoe.mirai.internal.contact.info.GroupInfoImpl
import net.mamoe.mirai.internal.network.protocol.packet.chat.NewContact
import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
import net.mamoe.mirai.internal.utils.*

View File

@ -23,6 +23,7 @@ import net.mamoe.mirai.event.events.GroupMessageSyncEvent
import net.mamoe.mirai.event.events.MemberCardChangeEvent
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.message.refine
import net.mamoe.mirai.internal.message.toMessageChainOnline
import net.mamoe.mirai.internal.network.Packet

View File

@ -23,6 +23,7 @@ import net.mamoe.mirai.contact.NormalMember
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.network.MultiPacketByIterable
import net.mamoe.mirai.internal.network.Packet

View File

@ -21,12 +21,13 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.data.FriendInfoImpl
import net.mamoe.mirai.data.GroupHonorType
import net.mamoe.mirai.event.broadcast
import net.mamoe.mirai.event.events.*
import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.*
import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
import net.mamoe.mirai.internal.network.MultiPacketBySequence
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidClient
@ -708,7 +709,7 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
return this.msgFrdRmk.asSequence().mapNotNull {
val friend = bot.getFriend(it.fuin) ?: return@mapNotNull null
val old: String
friend.checkIsFriendImpl().friendInfo.checkIsInfoImpl()
friend.checkIsFriendImpl().friendInfo
.also { info -> old = info.remark }
.remark = it.rmkName
// TODO: 2020/4/10 ADD REMARK QUERY

View File

@ -30,7 +30,7 @@ import net.mamoe.mirai.internal.utils.io.serialization.writeProtoBuf
internal class StrangerList {
object GetStrangerList : OutgoingPacketFactory<GetStrangerList.Response>("OidbSvc.0x5d2_0") {
class Response(val result: Int, val strangerList: List<Oidb0x5d2.FriendEntry>) : Packet {
class Response(val result: Int, val strangerList: List<Oidb0x5d2.FriendEntry>, val origin: Oidb0x5d2.RspGetList?) : Packet {
override fun toString(): String {
return "StrangerList.GetStrangerList.Response(result=$result)"
}
@ -61,10 +61,10 @@ internal class StrangerList {
if (pkg.result == 0) {
pkg.bodybuffer.loadAs(Oidb0x5d2.RspBody.serializer()).rspGetList!!.let {
bot.client.strangerSeq = it.seq
return Response(pkg.result, it.list)
return Response(pkg.result, it.list, it)
}
}
return Response(pkg.result, emptyList())
return Response(pkg.result, emptyList(), null)
}
}

View File

@ -25,6 +25,7 @@ import net.mamoe.mirai.internal.QQAndroidBot
import net.mamoe.mirai.internal.contact.appId
import net.mamoe.mirai.internal.createOtherClient
import net.mamoe.mirai.internal.message.contextualBugReportException
import net.mamoe.mirai.internal.network.FriendListCache
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.getRandomByteArray
@ -94,29 +95,30 @@ internal class StatSvc {
internal object Register : OutgoingPacketFactory<Register.Response>("StatSvc.register") {
internal object Response : Packet {
internal class Response(
val origin: SvcRespRegister
) : Packet {
override fun toString(): String = "Response(StatSvc.register)"
}
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): Response {
val packet = readUniPacket(SvcRespRegister.serializer())
if (packet.updateFlag.toInt() == 1) {
//TODO 加载好友列表
}
if (packet.largeSeqUpdate.toInt() == 1) {
//TODO 刷新好友列表
}
packet.iHelloInterval.let {
bot.configuration.heartbeatPeriodMillis = it.times(1000).toLong()
}
return Response
return Response(packet)
}
fun online(
client: QQAndroidClient,
regPushReason: RegPushReason = RegPushReason.appRegister
) = impl(client, 1 or 2 or 4, client.onlineStatus, regPushReason)
) = impl(client, 1 or 2 or 4, client.onlineStatus, regPushReason) {
client.bot.friendListCache?.let { friendListCache: FriendListCache ->
iLargeSeq = friendListCache.friendListSeq
// timeStamp = friendListCache.timeStamp
}
}
fun offline(
client: QQAndroidClient,
@ -127,7 +129,8 @@ internal class StatSvc {
client: QQAndroidClient,
bid: Long,
status: OnlineStatus,
regPushReason: RegPushReason = RegPushReason.appRegister
regPushReason: RegPushReason = RegPushReason.appRegister,
applyAction: SvcReqRegister.() -> Unit = {}
) = buildLoginOutgoingPacket(
client,
bodyType = 1,
@ -198,7 +201,7 @@ internal class StatSvc {
)
),
bSetMute = 0
)
).apply(applyAction)
)
)
)

View File

@ -0,0 +1,71 @@
/*
* Copyright 2020 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.utils
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.sample
import kotlin.coroutines.CoroutineContext
import kotlin.time.Duration
@OptIn(FlowPreview::class)
internal class ScheduledJob(
coroutineContext: CoroutineContext,
val interval: Duration,
private val task: suspend () -> Unit
) : CoroutineScope by CoroutineScope(coroutineContext + SupervisorJob(coroutineContext[Job])) {
private val coroutineExceptionHandler =
coroutineContext[CoroutineExceptionHandler].also {
requireNotNull(it) {
"Could not init ScheduledJob, coroutineExceptionHandler == null"
}
}
private val channel = Channel<Unit>(Channel.CONFLATED)
fun notice() {
if (interval == Duration.ZERO) {
launch { task() }
} else channel.offer(Unit)
}
private suspend fun doTask() {
runCatching {
task()
}.onFailure {
coroutineExceptionHandler!!.handleException(currentCoroutineContext(), it)
}
}
init {
if (interval != Duration.ZERO) {
launch {
channel.receiveAsFlow()
.runCatching {
sample(interval.toLongMilliseconds())
}
.fold(
onSuccess = { flow ->
flow.collect { doTask() }
},
onFailure = {
// binary change
while (isActive) {
delay(interval)
task()
}
}
)
}
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/master/LICENSE
*/
package net.mamoe.mirai.internal.utils
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.assertEquals
import kotlin.time.seconds
internal class ScheduledJobTest {
@Test
fun testScheduledJob() {
runBlocking {
val scope = CoroutineScope(CoroutineExceptionHandler { _, throwable ->
throwable.printStackTrace()
})
val invoked = AtomicInteger(0)
val job = ScheduledJob(scope.coroutineContext, 1.seconds) {
invoked.incrementAndGet()
}
delay(100)
assertEquals(0, invoked.get())
job.notice()
job.notice()
job.notice()
}
}
}