diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..d6dc7d8
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,14 @@
+# 3.24.0
+
+## 修复
+
+* 修复图片rkey导致链接失效的问题
+* 修复/get_image, /get_file 无法获取图片的问题
+* 修复上报他人管理员被取消通知
+
+## 新增
+
+* 新增表情回应发送和上报
+* 新增商城表情发送,和上报 url
+* 新增转发单条消息接口 `forward_friend_single_msg`, `forward_group_single_msg`
+* 新增新增好友事件
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
index d49a0d4..7f133a7 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,10 +1,10 @@
 {
   "manifest_version": 4,
   "type": "extension",
-  "name": "LLOneBot v3.23.0",
+  "name": "LLOneBot v3.24.0",
   "slug": "LLOneBot",
   "description": "使你的NTQQ支持OneBot11协议进行QQ机器人开发, 不支持商店在线更新",
-  "version": "3.23.0",
+  "version": "3.24.0",
   "icon": "./icon.jpg",
   "authors": [
     {
@@ -20,10 +20,14 @@
       "name": "LLOneBot.zip"
     }
   },
-  "platform": ["win32", "linux", "darwin"],
+  "platform": [
+    "win32",
+    "linux",
+    "darwin"
+  ],
   "injects": {
     "renderer": "./renderer/index.js",
     "main": "./main/main.cjs",
     "preload": "./preload/preload.cjs"
   }
-}
+}
\ No newline at end of file
diff --git a/src/common/data.ts b/src/common/data.ts
index 8c03020..0bd6744 100644
--- a/src/common/data.ts
+++ b/src/common/data.ts
@@ -3,6 +3,7 @@ import { type FileCache, type LLOneBotError } from './types'
 import { NTQQGroupApi } from '../ntqqapi/api/group'
 import { log } from './utils/log'
 import { isNumeric } from './utils/helper'
+import { NTQQFriendApi } from '../ntqqapi/api'
 
 export const selfInfo: SelfInfo = {
   uid: '',
@@ -24,14 +25,17 @@ export async function getFriend(uinOrUid: string): Promise<Friend | undefined> {
   let filterKey = isNumeric(uinOrUid.toString()) ? 'uin' : 'uid'
   let filterValue = uinOrUid
   let friend = friends.find((friend) => friend[filterKey] === filterValue.toString())
-  // if (!friend) {
-  //     try {
-  //         friends = (await NTQQApi.getFriends(true))
-  //         friend = friends.find(friend => friend[filterKey] === filterValue.toString())
-  //     } catch (e) {
-  //         // log("刷新好友列表失败", e.stack.toString())
-  //     }
-  // }
+  if (!friend) {
+    try {
+      const _friends = (await NTQQFriendApi.getFriends(true))
+      friend = _friends.find(friend => friend[filterKey] === filterValue.toString())
+      if (friend){
+        friends.push(friend)
+      }
+    } catch (e) {
+      log("刷新好友列表失败", e.stack.toString())
+    }
+  }
   return friend
 }
 
@@ -44,7 +48,8 @@ export async function getGroup(qq: string): Promise<Group | undefined> {
       if (group) {
         groups.push(group)
       }
-    } catch (e) {}
+    } catch (e) {
+    }
   }
   return group
 }
diff --git a/src/main/main.ts b/src/main/main.ts
index 14d0cf8..854ae3b 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -157,6 +157,7 @@ function onLoad() {
     const { debug, reportSelfMessage } = getConfigUtil().getConfig()
     for (let message of msgList) {
       // 过滤启动之前的消息
+      // log('收到新消息', message);
       if (parseInt(message.msgTime) < startTime / 1000) {
         continue
       }
@@ -191,6 +192,12 @@ function onLoad() {
           postOB11Event(groupEvent)
         }
       })
+      OB11Constructor.FriendAddEvent(message).then((friendAddEvent) => {
+        if (friendAddEvent) {
+          // log("post friend add event", friendAddEvent);
+          postOB11Event(friendAddEvent)
+        }
+      })
     }
   }
 
@@ -219,7 +226,7 @@ function onLoad() {
     })
     registerReceiveHook<{ msgList: Array<RawMessage> }>([ReceiveCmdS.UPDATE_MSG], async (payload) => {
       for (const message of payload.msgList) {
-        // log("message update", message.sendStatus, message.msgId, message.msgSeq)
+        // log("message update", message)
         if (message.recallTime != '0') {
           //todo: 这个判断方法不太好,应该使用灰色消息元素来判断
           // 撤回消息上报
@@ -304,7 +311,7 @@ function onLoad() {
             // if (notify.user2.uid) {
             //     member2 = await getGroupMember(notify.group.groupCode, null, notify.user2.uid);
             // }
-            if ([GroupNotifyTypes.ADMIN_SET, GroupNotifyTypes.ADMIN_UNSET].includes(notify.type)) {
+            if ([GroupNotifyTypes.ADMIN_SET, GroupNotifyTypes.ADMIN_UNSET, GroupNotifyTypes.ADMIN_UNSET_OTHER].includes(notify.type)) {
               const member1 = await getGroupMember(notify.group.groupCode, notify.user1.uid)
               log('有管理员变动通知')
               refreshGroupMembers(notify.group.groupCode).then()
@@ -314,7 +321,7 @@ function onLoad() {
               if (member1) {
                 log('变动管理员获取成功')
                 groupAdminNoticeEvent.user_id = parseInt(member1.uin)
-                groupAdminNoticeEvent.sub_type = notify.type == GroupNotifyTypes.ADMIN_UNSET ? 'unset' : 'set'
+                groupAdminNoticeEvent.sub_type = [GroupNotifyTypes.ADMIN_UNSET, GroupNotifyTypes.ADMIN_UNSET_OTHER].includes(notify.type) ? 'unset' : 'set'
                 // member1.role = notify.type == GroupNotifyTypes.ADMIN_SET ? GroupMemberRole.admin : GroupMemberRole.normal;
                 postOB11Event(groupAdminNoticeEvent, true)
               } else {
diff --git a/src/ntqqapi/api/friend.ts b/src/ntqqapi/api/friend.ts
index 1604c29..cfa2daf 100644
--- a/src/ntqqapi/api/friend.ts
+++ b/src/ntqqapi/api/friend.ts
@@ -2,6 +2,7 @@ import { Friend, FriendRequest } from '../types'
 import { ReceiveCmdS } from '../hook'
 import { callNTQQApi, GeneralCallResult, NTQQApiMethod } from '../ntcall'
 import { friendRequests } from '../../common/data'
+import { log } from '../../common/utils'
 
 export class NTQQFriendApi {
   static async getFriends(forced = false) {
@@ -16,7 +17,9 @@ export class NTQQFriendApi {
       methodName: NTQQApiMethod.FRIENDS,
       args: [{ force_update: forced }, undefined],
       cbCmd: ReceiveCmdS.FRIENDS,
+      afterFirstCmd: false,
     })
+    // log('获取好友列表', data)
     let _friends: Friend[] = []
     for (const fData of data.data) {
       _friends.push(...fData.buddyList)
diff --git a/src/ntqqapi/constructor.ts b/src/ntqqapi/constructor.ts
index 6d7c424..e8b9b8b 100644
--- a/src/ntqqapi/constructor.ts
+++ b/src/ntqqapi/constructor.ts
@@ -23,6 +23,8 @@ import { defaultVideoThumb, getVideoInfo } from '../common/utils/video'
 import { encodeSilk } from '../common/utils/audio'
 import { isNull } from '../common/utils'
 
+export const mFaceCache = new Map<string, string>(); // emojiId -> faceName
+
 export class SendMsgElementConstructor {
   static poke(groupCode: string, uin: string) {
     return null
@@ -117,7 +119,15 @@ export class SendMsgElementConstructor {
   }
 
   static async video(filePath: string, fileName: string = '', diyThumbPath: string = ''): Promise<SendVideoElement> {
+    try{
+      await fs.stat(filePath)
+    }catch (e) {
+      throw `文件${filePath}异常,不存在`
+    }
+    log("复制视频到QQ目录", filePath)
     let { fileName: _fileName, path, fileSize, md5 } = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO)
+
+    log("复制视频到QQ目录完成", path)
     if (fileSize === 0) {
       throw '文件异常,大小为0'
     }
@@ -184,7 +194,7 @@ export class SendMsgElementConstructor {
     })
     let thumbPath = new Map()
     const _thumbPath = await createThumb
-    log('生成缩略图', _thumbPath)
+    log('生成视频缩略图', _thumbPath)
     const thumbSize = (await fs.stat(_thumbPath)).size
     // log("生成缩略图", _thumbPath)
     thumbPath.set(0, _thumbPath)
@@ -273,7 +283,7 @@ export class SendMsgElementConstructor {
         emojiPackageId,
         emojiId,
         key,
-        faceName
+        faceName: faceName || mFaceCache.get(emojiId) || '[商城表情]',
       },
     }
   }
diff --git a/src/ntqqapi/types/msg.ts b/src/ntqqapi/types/msg.ts
index 6978f24..f21d008 100644
--- a/src/ntqqapi/types/msg.ts
+++ b/src/ntqqapi/types/msg.ts
@@ -372,6 +372,8 @@ export interface MultiForwardMsgElement {
 
 export interface RawMessage {
   msgId: string
+  msgType: number
+  subMsgType: number
   msgShortId?: number // 自己维护的消息id
   msgTime: string // 时间戳,秒
   msgSeq: string
diff --git a/src/ntqqapi/types/notify.ts b/src/ntqqapi/types/notify.ts
index 2da2f68..c568dfc 100644
--- a/src/ntqqapi/types/notify.ts
+++ b/src/ntqqapi/types/notify.ts
@@ -5,7 +5,8 @@ export enum GroupNotifyTypes {
   ADMIN_SET = 8,
   KICK_MEMBER = 9,
   MEMBER_EXIT = 11, // 主动退出
-  ADMIN_UNSET = 12,
+  ADMIN_UNSET = 12,  // 我被取消管理员
+  ADMIN_UNSET_OTHER = 13,  // 其他人取消管理员
 }
 
 export interface GroupNotifies {
diff --git a/src/onebot11/action/group/GetGroupList.ts b/src/onebot11/action/group/GetGroupList.ts
index f27ef0c..b5c2ae5 100644
--- a/src/onebot11/action/group/GetGroupList.ts
+++ b/src/onebot11/action/group/GetGroupList.ts
@@ -7,7 +7,7 @@ import { NTQQGroupApi } from '../../../ntqqapi/api'
 import { log } from '../../../common/utils'
 
 interface Payload {
-  no_cache: boolean
+  no_cache: boolean | string
 }
 
 class GetGroupList extends BaseAction<Payload, OB11Group[]> {
@@ -16,11 +16,11 @@ class GetGroupList extends BaseAction<Payload, OB11Group[]> {
   protected async _handle(payload: Payload) {
     if (
       groups.length === 0
-      // || payload.no_cache === true
+      || payload?.no_cache === true || payload?.no_cache === 'true'
     ) {
       try {
         const groups = await NTQQGroupApi.getGroups(true)
-        // log("get groups", groups)
+        log("强制刷新群列表, 数量:", groups.length)
         return OB11Constructor.groups(groups)
       } catch (e) {}
     }
diff --git a/src/onebot11/action/group/GetGroupMemberList.ts b/src/onebot11/action/group/GetGroupMemberList.ts
index da7555c..2c541f8 100644
--- a/src/onebot11/action/group/GetGroupMemberList.ts
+++ b/src/onebot11/action/group/GetGroupMemberList.ts
@@ -4,9 +4,11 @@ import { OB11Constructor } from '../../constructor'
 import BaseAction from '../BaseAction'
 import { ActionName } from '../types'
 import { NTQQGroupApi } from '../../../ntqqapi/api/group'
+import { log } from '../../../common/utils'
 
 export interface PayloadType {
-  group_id: number
+  group_id: number,
+  no_cache: boolean | string
 }
 
 class GetGroupMemberList extends BaseAction<PayloadType, OB11GroupMember[]> {
@@ -15,8 +17,9 @@ class GetGroupMemberList extends BaseAction<PayloadType, OB11GroupMember[]> {
   protected async _handle(payload: PayloadType) {
     const group = await getGroup(payload.group_id.toString())
     if (group) {
-      if (!group.members?.length) {
+      if (!group.members?.length || payload.no_cache === true || payload.no_cache === 'true') {
         group.members = await NTQQGroupApi.getGroupMembers(payload.group_id.toString())
+        log('强制刷新群成员列表, 数量: ', group.members.length)
       }
       return OB11Constructor.groupMembers(group)
     } else {
diff --git a/src/onebot11/action/msg/DeleteMsg.ts b/src/onebot11/action/msg/DeleteMsg.ts
index c85d6d5..d1bc1c8 100644
--- a/src/onebot11/action/msg/DeleteMsg.ts
+++ b/src/onebot11/action/msg/DeleteMsg.ts
@@ -12,6 +12,9 @@ class DeleteMsg extends BaseAction<Payload, void> {
 
   protected async _handle(payload: Payload) {
     let msg = await dbUtil.getMsgByShortId(payload.message_id)
+    if (!msg) {
+      throw `消息${payload.message_id}不存在`
+    }
     await NTQQMsgApi.recallMsg(
       {
         chatType: msg.chatType,
diff --git a/src/onebot11/action/msg/SendMsg.ts b/src/onebot11/action/msg/SendMsg.ts
index 305474c..ad21e79 100644
--- a/src/onebot11/action/msg/SendMsg.ts
+++ b/src/onebot11/action/msg/SendMsg.ts
@@ -179,7 +179,7 @@ export async function createSendElements(
         break
       case OB11MessageDataType.mface: {
         sendElements.push(
-          SendMsgElementConstructor.mface(sendMsg.data.emoji_package_id, sendMsg.data.emoji_id, sendMsg.data.key, sendMsg.data.summary || ""),
+          SendMsgElementConstructor.mface(sendMsg.data.emoji_package_id, sendMsg.data.emoji_id, sendMsg.data.key, sendMsg.data.summary),
         )
       }
       case OB11MessageDataType.image:
diff --git a/src/onebot11/action/user/GetFriendList.ts b/src/onebot11/action/user/GetFriendList.ts
index 19b0b9c..00ee660 100644
--- a/src/onebot11/action/user/GetFriendList.ts
+++ b/src/onebot11/action/user/GetFriendList.ts
@@ -3,11 +3,25 @@ import { OB11Constructor } from '../../constructor'
 import { friends } from '../../../common/data'
 import BaseAction from '../BaseAction'
 import { ActionName } from '../types'
+import { NTQQFriendApi } from '../../../ntqqapi/api'
+import { log } from '../../../common/utils'
 
-class GetFriendList extends BaseAction<null, OB11User[]> {
+interface Payload{
+  no_cache: boolean | string
+}
+
+class GetFriendList extends BaseAction<Payload, OB11User[]> {
   actionName = ActionName.GetFriendList
 
-  protected async _handle(payload: null) {
+  protected async _handle(payload: Payload) {
+    if (friends.length === 0 || payload?.no_cache === true || payload?.no_cache === 'true') {
+      const _friends = await NTQQFriendApi.getFriends(true)
+      // log('强制刷新好友列表,结果: ', _friends)
+      if (_friends.length > 0) {
+        friends.length = 0
+        friends.push(..._friends)
+      }
+    }
     return OB11Constructor.friends(friends)
   }
 }
diff --git a/src/onebot11/constructor.ts b/src/onebot11/constructor.ts
index 36acc8a..5d43688 100644
--- a/src/onebot11/constructor.ts
+++ b/src/onebot11/constructor.ts
@@ -44,6 +44,8 @@ import { OB11GroupCardEvent } from './event/notice/OB11GroupCardEvent'
 import { OB11GroupDecreaseEvent } from './event/notice/OB11GroupDecreaseEvent'
 import { NTQQGroupApi } from '../ntqqapi/api'
 import { OB11GroupMsgEmojiLikeEvent } from './event/notice/OB11MsgEmojiLikeEvent'
+import { mFaceCache } from '../ntqqapi/constructor'
+import { OB11FriendAddNoticeEvent } from './event/notice/OB11FriendAddNoticeEvent'
 
 let lastRKeyUpdateTime = 0
 
@@ -247,6 +249,7 @@ export class OB11Constructor {
         message_data['data']['emoji_id'] = element.marketFaceElement.emojiId
         message_data['data']['emoji_package_id'] = String(element.marketFaceElement.emojiPackageId)
         message_data['data']['key'] = element.marketFaceElement.key
+        mFaceCache.set(md5, element.marketFaceElement.faceName);
       } else if (element.markdownElement) {
         message_data['type'] = OB11MessageDataType.markdown
         message_data['data']['data'] = element.markdownElement.content
@@ -458,6 +461,16 @@ export class OB11Constructor {
     }
   }
 
+  static async FriendAddEvent(msg: RawMessage): Promise<OB11FriendAddNoticeEvent | undefined> {
+    if (msg.chatType !== ChatType.friend) {
+      return;
+    }
+    if (msg.msgType === 5 && msg.subMsgType === 12) {
+      const event = new OB11FriendAddNoticeEvent(parseInt(msg.peerUin));
+      return event;
+    }
+    return;
+  }
   static friend(friend: User): OB11User {
     return {
       user_id: parseInt(friend.uin),
diff --git a/src/onebot11/event/notice/OB11FriendAddNoticeEvent.ts b/src/onebot11/event/notice/OB11FriendAddNoticeEvent.ts
new file mode 100644
index 0000000..25ea23a
--- /dev/null
+++ b/src/onebot11/event/notice/OB11FriendAddNoticeEvent.ts
@@ -0,0 +1,11 @@
+import { OB11BaseNoticeEvent } from './OB11BaseNoticeEvent';
+
+export class OB11FriendAddNoticeEvent extends OB11BaseNoticeEvent {
+  notice_type = 'friend_add';
+  user_id: number;
+
+  public constructor(userId: number) {
+    super();
+    this.user_id = userId;
+  }
+}
diff --git a/src/renderer/index.ts b/src/renderer/index.ts
index 4dc969f..bee2b41 100644
--- a/src/renderer/index.ts
+++ b/src/renderer/index.ts
@@ -165,12 +165,12 @@ async function onSettingWindowCreated(view: Element) {
           }</span>`,
           SettingButton('选择ffmpeg', 'config-ffmpeg-select'),
         ),
-        // SettingItem(
-        //   '音乐卡片签名地址',
-        //   null,
-        //   `<div class="q-input" style="width:210px;"><input class="q-input__inner" data-config-key="musicSignUrl" type="text" value="${config.musicSignUrl}" placeholder="未设置" /></div>`,
-        //   'config-musicSignUrl',
-        // ),
+        SettingItem(
+          '音乐卡片签名地址',
+          null,
+          `<div class="q-input" style="width:210px;"><input class="q-input__inner" data-config-key="musicSignUrl" type="text" value="${config.musicSignUrl}" placeholder="未设置" /></div>`,
+          'config-musicSignUrl',
+        ),
         SettingItem('', null, SettingButton('保存', 'config-ob11-save', 'primary')),
       ]),
       SettingList([
diff --git a/src/version.ts b/src/version.ts
index 9d5ae7c..68f0fe0 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1 +1 @@
-export const version = '3.23.0'
+export const version = '3.24.0'