├── .eslintrc.json ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── assets ├── console-theme │ ├── app.js │ └── style.css ├── default-theme │ ├── app.js │ └── style.css ├── faces │ ├── 0.png │ ├── 1.png │ ├── 10.png │ ├── 100.png │ ├── 101.png │ ├── 102.png │ ├── 103.png │ ├── 104.png │ ├── 105.png │ ├── 106.png │ ├── 107.png │ ├── 108.png │ ├── 109.png │ ├── 11.png │ ├── 110.png │ ├── 111.png │ ├── 112.png │ ├── 113.png │ ├── 114.png │ ├── 115.png │ ├── 116.png │ ├── 117.png │ ├── 118.png │ ├── 119.png │ ├── 12.png │ ├── 120.png │ ├── 121.png │ ├── 122.png │ ├── 123.png │ ├── 124.png │ ├── 125.png │ ├── 126.png │ ├── 127.png │ ├── 128.png │ ├── 129.png │ ├── 13.png │ ├── 130.png │ ├── 131.png │ ├── 132.png │ ├── 133.png │ ├── 134.png │ ├── 135.png │ ├── 136.png │ ├── 137.png │ ├── 138.png │ ├── 139.png │ ├── 14.png │ ├── 140.png │ ├── 141.png │ ├── 142.png │ ├── 143.png │ ├── 144.png │ ├── 145.png │ ├── 146.png │ ├── 147.png │ ├── 148.png │ ├── 149.png │ ├── 15.png │ ├── 150.png │ ├── 151.png │ ├── 152.png │ ├── 153.png │ ├── 154.png │ ├── 155.png │ ├── 156.png │ ├── 157.png │ ├── 158.png │ ├── 159.png │ ├── 16.png │ ├── 160.png │ ├── 161.png │ ├── 162.png │ ├── 163.png │ ├── 164.png │ ├── 165.png │ ├── 166.png │ ├── 167.png │ ├── 168.png │ ├── 169.png │ ├── 17.png │ ├── 170.png │ ├── 171.png │ ├── 172.png │ ├── 173.png │ ├── 174.png │ ├── 175.png │ ├── 176.png │ ├── 177.png │ ├── 178.png │ ├── 179.png │ ├── 18.png │ ├── 180.png │ ├── 181.png │ ├── 182.png │ ├── 183.png │ ├── 184.png │ ├── 185.png │ ├── 186.png │ ├── 187.png │ ├── 188.png │ ├── 189.png │ ├── 19.png │ ├── 190.png │ ├── 191.png │ ├── 192.png │ ├── 193.png │ ├── 194.png │ ├── 195.png │ ├── 196.png │ ├── 197.png │ ├── 198.png │ ├── 199.png │ ├── 2.png │ ├── 20.png │ ├── 200.png │ ├── 201.png │ ├── 202.png │ ├── 203.png │ ├── 204.png │ ├── 205.png │ ├── 206.png │ ├── 207.png │ ├── 208.png │ ├── 209.png │ ├── 21.png │ ├── 210.png │ ├── 211.png │ ├── 212.png │ ├── 213.png │ ├── 214.png │ ├── 215.png │ ├── 216.png │ ├── 217.png │ ├── 218.png │ ├── 219.png │ ├── 22.png │ ├── 220.png │ ├── 221.png │ ├── 222.png │ ├── 223.png │ ├── 224.png │ ├── 225.png │ ├── 226.png │ ├── 227.png │ ├── 228.png │ ├── 229.png │ ├── 23.png │ ├── 230.png │ ├── 231.png │ ├── 232.png │ ├── 233.png │ ├── 234.png │ ├── 235.png │ ├── 236.png │ ├── 237.png │ ├── 238.png │ ├── 239.png │ ├── 24.png │ ├── 240.png │ ├── 241.png │ ├── 242.png │ ├── 243.png │ ├── 244.png │ ├── 245.png │ ├── 246.png │ ├── 247.png │ ├── 25.png │ ├── 26.png │ ├── 260.png │ ├── 261.png │ ├── 262.png │ ├── 263.png │ ├── 264.png │ ├── 265.png │ ├── 266.png │ ├── 267.png │ ├── 268.png │ ├── 269.png │ ├── 27.png │ ├── 270.png │ ├── 271.png │ ├── 272.png │ ├── 273.png │ ├── 274.png │ ├── 276.png │ ├── 277.png │ ├── 278.png │ ├── 279.png │ ├── 28.png │ ├── 280.png │ ├── 281.png │ ├── 282.png │ ├── 283.png │ ├── 284.png │ ├── 285.png │ ├── 286.png │ ├── 287.png │ ├── 288.png │ ├── 289.png │ ├── 29.png │ ├── 290.png │ ├── 291.png │ ├── 292.png │ ├── 293.png │ ├── 294.png │ ├── 295.png │ ├── 296.png │ ├── 297.png │ ├── 298.png │ ├── 299.png │ ├── 3.png │ ├── 30.png │ ├── 300.png │ ├── 301.png │ ├── 302.png │ ├── 303.png │ ├── 304.png │ ├── 305.png │ ├── 306.png │ ├── 307.png │ ├── 308.png │ ├── 309.png │ ├── 31.png │ ├── 310.png │ ├── 311.png │ ├── 312.png │ ├── 313.png │ ├── 314.png │ ├── 315.png │ ├── 316.png │ ├── 317.png │ ├── 318.png │ ├── 319.png │ ├── 32.png │ ├── 320.png │ ├── 321.png │ ├── 322.png │ ├── 323.png │ ├── 324.png │ ├── 33.png │ ├── 34.png │ ├── 35.png │ ├── 36.png │ ├── 37.png │ ├── 38.png │ ├── 39.png │ ├── 4.png │ ├── 40.png │ ├── 41.png │ ├── 42.png │ ├── 43.png │ ├── 44.png │ ├── 45.png │ ├── 46.png │ ├── 47.png │ ├── 48.png │ ├── 49.png │ ├── 5.png │ ├── 50.png │ ├── 51.png │ ├── 52.png │ ├── 53.png │ ├── 54.png │ ├── 55.png │ ├── 56.png │ ├── 57.png │ ├── 58.png │ ├── 59.png │ ├── 6.png │ ├── 60.png │ ├── 61.png │ ├── 62.png │ ├── 63.png │ ├── 64.png │ ├── 65.png │ ├── 66.png │ ├── 67.png │ ├── 68.png │ ├── 69.png │ ├── 7.png │ ├── 70.png │ ├── 71.png │ ├── 72.png │ ├── 73.png │ ├── 74.png │ ├── 75.png │ ├── 76.png │ ├── 77.png │ ├── 78.png │ ├── 79.png │ ├── 8.png │ ├── 80.png │ ├── 81.png │ ├── 82.png │ ├── 83.png │ ├── 84.png │ ├── 85.png │ ├── 86.png │ ├── 87.png │ ├── 88.png │ ├── 89.png │ ├── 9.png │ ├── 90.png │ ├── 91.png │ ├── 92.png │ ├── 93.png │ ├── 94.png │ ├── 95.png │ ├── 96.png │ ├── 97.png │ ├── 98.png │ └── 99.png ├── preload.js └── types.d.ts ├── ico.ico ├── package-lock.json ├── package.json ├── preview.gif ├── src ├── cdp.ts ├── chat.ts ├── client.ts ├── config.ts ├── explorer.ts ├── extension.ts ├── global.ts └── test │ ├── runTest.ts │ └── suite │ ├── extension.test.ts │ └── index.ts └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/semi": "warn", 13 | "curly": "warn", 14 | "eqeqeq": "warn", 15 | "no-throw-literal": "warn", 16 | "semi": "off" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | - test 9 | 10 | jobs: 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - run: npm install 17 | - run: npm install vsce --save-dev 18 | - run: ./node_modules/.bin/vsce package 19 | - uses: actions/upload-artifact@v2 20 | with: 21 | name: vscode-qq-pre.vsix 22 | path: ./vscode-qq-* 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | .vscode-test 5 | *.vsix 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceFolder}" 11 | ], 12 | "name": "Launch Extension", 13 | "outFiles": [ 14 | "${workspaceFolder}/out/**/*.js" 15 | ], 16 | "preLaunchTask": "npm", 17 | "request": "launch", 18 | "type": "pwa-extensionHost" 19 | }, 20 | { 21 | "name": "Run Extension", 22 | "type": "extensionHost", 23 | "request": "launch", 24 | "args": [ 25 | "--extensionDevelopmentPath=${workspaceFolder}" 26 | ], 27 | "outFiles": [ 28 | "${workspaceFolder}/out/**/*.js" 29 | ], 30 | "preLaunchTask": "${defaultBuildTask}" 31 | }, 32 | { 33 | "name": "Extension Tests", 34 | "type": "extensionHost", 35 | "request": "launch", 36 | "args": [ 37 | "--extensionDevelopmentPath=${workspaceFolder}", 38 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 39 | ], 40 | "outFiles": [ 41 | "${workspaceFolder}/out/test/**/*.js" 42 | ], 43 | "preLaunchTask": "${defaultBuildTask}" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | .yarnrc 7 | vsc-extension-quickstart.md 8 | **/tsconfig.json 9 | **/.eslintrc.json 10 | **/*.map 11 | **/*.ts 12 | preview.gif 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # VS Code QQ Extension Change Log 2 | 3 | ## Version 1.4.1: 2021/10/9 4 | 5 | * [default-theme] 修复表情排列问题 6 | * [default-theme] 修复无法显示好友文件消息 7 | * [Explorer] 添加 `@feecback` 选项 8 | 9 | ## Version 1.4.0: 2021/9/18 10 | 11 | * [default-theme] 支持查看嵌套转发内的消息 12 | * [default-theme] 支持url染色和点击 13 | * [default-theme] 表情以小图形式发送 14 | * [default-theme] 自动同步其他客户端发送的私聊 15 | * 更新依赖库,修复已知bug 16 | 17 | ## Version 1.3.3: 2021/8/10 18 | 19 | * [default-theme] 定位到`[回复]`和`[撤回]`的消息时显示动画效果 20 | 21 | ## Version 1.3.2: 2021/7/23 22 | 23 | * 修复每次登录都要扫码的问题 24 | * 放大二维码 25 | 26 | ## Version 1.3.1: 2021/7/22 27 | 28 | * [Explorer] 支持手机QQ扫码直接登录 29 | * [default-theme] 修复头像缓存无法及时更新的问题 30 | * [default-theme] 支持直接粘贴图片 31 | * [default-theme] 支持接收链接分享 32 | * [default-theme] 优化了UI 33 | 34 | ## Version 1.2.4: 2021/6/6 35 | 36 | * 修复有时无法验证设备锁的问题 37 | * [theme] 修复月份显示少1的问题 38 | * [theme] 群头像改为方的 39 | 40 | ## Version 1.2.3: 2021/5/19 41 | 42 | * [Explorer] 显示好友备注 43 | * [face] 新增13个表情 44 | * [theme] 修复自定义css/js文件无法载入的问题 45 | * [theme] 使用 `"Referrer Policy: no-referrer"` 46 | 47 | ## Version 1.2.2: 2021/5/2 48 | 49 | * 增加账号重复登录检测,以避免出现多个Code中重复登录的情况 50 | 51 | ## Version 1.2.1: 2021/4/27 52 | 53 | * [default-theme] 头像和图片使用 `https` url 54 | * 修复一些体验方面的问题 55 | 56 | ## Version 1.2.0: 2021/4/18 57 | 58 | * [default-theme] 支持查看合并转发,以及各种xml/json卡片消息 59 | * [default-theme] 新增回复、戳一戳、撤回、禁言、踢人、设置管理员等功能 60 | * [default-theme] 新增侧栏用于查看群成员,以及对其进行操作 61 | * [default-theme] 优化了内存占用 62 | * [Explorer] 右键栏新增`邀请好友入群` 63 | * [Explorer] 修改了群和好友图标,以方便辨识 64 | * [Command] 命令面板新增`群搜索`和`好友搜索` 65 | 66 | ## Version 1.1.0: 2021/4/12 67 | 68 | * 漫游表情包支持 69 | * 完成了前端接口的抽象 70 | * [Explorer] 命令面板添加了 `QQ: Login` 71 | 72 | ## Version 1.0.0: 2021/4/7 73 | 74 | * [console-theme] 添加一枚console风格的主题 75 | * [default-theme] 调节色彩与vscode所使用的主题一致 76 | * [default-theme] 优化图片预览 77 | * [Explorer] 修复设备锁验证会黑屏的问题 78 | 79 | ## Version 0.4.0: 2021/4/4 80 | 81 | * [default-theme] 增加表情、颜文字、图片的选择框和按钮 82 | * [default-theme] 支持查看头像 83 | * [default-theme] 输入框的颜色和vscode主题保持一致 84 | * [Explorer] 优化性能 85 | 86 | ## Version 0.3.0: 2021/4/2 87 | 88 | * [Config] 增加 `theme` `theme_css` `theme_js` 配置项,以支持自定义UI界面 89 | * [default-theme] 优化UI,双击表情可以加入输入框 90 | 91 | ## Version 0.2.0: 2021/3/30 92 | 93 | * [ChatView] 群内双击名字可以at 94 | * [ChatView] 支持经典表情 95 | * [ChatView] 可以将表情和图片拖至输入框 96 | * [ChatView] 支持文件、语音和视频跳转下载 97 | * [ChatView] 优化滚动条体验 98 | * [ChatView] 消息发送失败会有提示(如被禁言时长等) 99 | * [Explorer] "有新消息" 改为 "+消息数" 100 | * [Explorer] 支持查看和设置个人资料 101 | * [Explorer] 支持查看好友资料和群资料 102 | * [Explorer] 优化menu排序,"置顶" 改为 "固定" 103 | 104 | ## Version 0.1.1 105 | 106 | * fix xss注入 107 | * fix 一些UI细节问题 108 | * add Ctrl+Enter发送 109 | 110 | ## Version 0.1.0: 2021/3/26 111 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VS Code QQ Extension 2 | 3 | [![discord](https://img.shields.io/static/v1?label=chat&message=discord&color=7289da&logo=discord)](https://discord.gg/gKnU7BARzv) | **[Repository](https://github.com/takayama-lily/vscode-qq)** | **[Offline Installers](https://github.com/takayama-lily/vscode-qq/releases)** | vscode >= 1.53.0 | 摸鱼工具 4 | 5 | > 本程序不在本地保存任何消息记录和图片。暂不支持临时会话。 6 | > `@设置` 里的 `platform` 是登录协议,1:手机 3:手表(功能不完整) 4:PC 5:pad(默认) 7 | 8 | ## 切换UI主题 9 | 10 | * 当前支持两种主题 `default`(默认) 、`console`(控制台风格) 11 | * 可在`@设置`中修改 `"theme": "console"` 来切换 12 | * 上级玩家可以 [修改/自定义UI主题](https://github.com/takayama-lily/vscode-qq/wiki/%E8%87%AA%E5%AE%9A%E4%B9%89%E8%81%8A%E5%A4%A9UI%E7%95%8C%E9%9D%A2) 13 | 14 | ## 可用命令 15 | 16 | > Ctrl+Shift+P 打开命令面板 17 | 18 | * QQ Explorer: Login 19 | * QQ Explorer: 搜索好友 20 | * QQ Explorer: 搜索群 21 | 22 | ## 其他 23 | 24 | * 如何清除登录信息 25 | 1. 登录状态下点击 `@切换账号` 26 | 2. 关闭或重启vscode即可完全清除 27 | * [外网被限制无法登录的解决方法](https://github.com/takayama-lily/vscode-qq/wiki/%E6%88%91%E7%9A%84%E6%9C%BA%E5%99%A8%E6%B2%A1%E6%9C%89%E5%A4%96%E7%BD%91%E6%80%8E%E4%B9%88%E5%8A%9E) 28 | 29 | ---- 30 | 31 | ![预览图](https://raw.githubusercontent.com/takayama-lily/vscode-qq/master/preview.gif) 32 | 33 | > 使用的UI库:[https://github.com/MorFansLab/LiteWebChat_Frame](https://github.com/MorFansLab/LiteWebChat_Frame) 34 | > QQ协议库:[https://github.com/takayama-lily/oicq](https://github.com/takayama-lily/oicq) 35 | 36 | ---- 37 | 38 | ## 自行编译此扩展 39 | 40 | ```bash 41 | # Node.js版本需高于12.16 42 | # clone此项目 43 | > npm i 44 | > npm i typescript -g 45 | > npm i vsce -g 46 | > vsce package 47 | ``` 48 | -------------------------------------------------------------------------------- /assets/console-theme/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * window.webview 是一个内置全局变量,封装了一些与宿主交互的方法 3 | * @type {import("../types").Webview} 4 | */ 5 | var webview; 6 | 7 | // 监听消息和通知 8 | webview.on("message", (data) => { 9 | const msg = data.detail; 10 | document.querySelector("#console").insertAdjacentHTML("beforeend", genUserMessage(msg)); 11 | webview.scrollEnd(); 12 | }); 13 | webview.on("notice", (data) => { 14 | const msg = data.detail; 15 | document.querySelector("#console").insertAdjacentHTML("beforeend", genSystemMessage(msg)); 16 | webview.scrollEnd(); 17 | }); 18 | 19 | function sendMsg() { 20 | const message = document.querySelector("#commandline").value; 21 | if (!message) { 22 | return; 23 | } 24 | webview.sendMsg(message).then((data) => { 25 | // 发送失败 26 | if (data.retcode > 1) { 27 | document.querySelector("#console").insertAdjacentHTML("beforeend", `
28 | 29 | [ERROR] 30 | - ${new Date} - ${data.error?.message} 31 | 32 |
`); 33 | return; 34 | } 35 | 36 | // 私聊需要自己打印消息 37 | if (webview.c2c && data.data.message_id) { 38 | const html = `
39 | 40 | [INFO] 41 | - ${webview.datetime()} - 42 | me<${webview.self_uin}>: 43 |
44 |
${filterXss(message)}
45 |
`; 46 | document.querySelector("#console").insertAdjacentHTML("beforeend", html); 47 | } 48 | document.querySelector("#commandline").value = ""; 49 | }).catch(() => { 50 | document.querySelector("#commandline").value = ""; 51 | }).finally(() => { 52 | webview.scrollEnd(); 53 | }); 54 | } 55 | 56 | /** 57 | * xss过滤 58 | * @param {string} str 59 | */ 60 | function filterXss(str) { 61 | return str.replace(/&/g, "&").replace(//g, ">"); 62 | } 63 | 64 | /** 65 | * 生成聊天消息 66 | * @param {import("oicq").PrivateMessageEventData | import("oicq").GroupMessageEventData} data 67 | */ 68 | function genUserMessage(data) { 69 | return `
70 | 71 | [INFO] 72 | - ${webview.datetime(data.time)} - 73 | ${filterXss(data.sender.card ? data.sender.card : data.sender.nickname)}<${data.user_id}>: 74 |
75 |
${filterXss(data.raw_message)}
76 |
`; 77 | } 78 | 79 | /** 80 | * 生成系统消息 81 | * @param {import("oicq").GroupNoticeEventData | import("oicq").FriendNoticeEventData} data 82 | */ 83 | function genSystemMessage(data) { 84 | let msg = ""; 85 | if (data.notice_type === "group") { 86 | switch (data.sub_type) { 87 | case "increase": 88 | msg = `${data.user_id} joined the group.`; 89 | break; 90 | case "decrease": 91 | if (data.dismiss) { 92 | msg = `This group is dismissed`; 93 | } else { 94 | msg = `${data.user_id} left from the group`; 95 | } 96 | break; 97 | case "ban": 98 | if (data.user_id > 0) 99 | msg = `${data.operator_id} muted ${data.user_id} ${data.duration} seconds.`; 100 | else 101 | msg = `${data.operator_id} ${data.duration > 0 ? "enabled" : "disabled"} muteAll.`; 102 | break; 103 | } 104 | } 105 | if (!msg) { 106 | return ""; 107 | } 108 | return `
109 | 110 | [NOTICE] 111 | - ${webview.datetime(data.time)} - ${msg} 112 | 113 |
`; 114 | } 115 | 116 | // Ctrl+Enter 117 | window.onkeydown = function (event) { 118 | if (event.ctrlKey && event.keyCode === 13) { 119 | sendMsg(); 120 | } 121 | }; 122 | 123 | //init 124 | document.querySelector("body").insertAdjacentHTML("beforeend", `
125 |
126 | 127 |
`); 128 | 129 | //加载10条历史消息 130 | webview.getChatHistory("", 10).then((data) => { 131 | let html = ""; 132 | for (let msg of data.data) { 133 | html += genUserMessage(msg); 134 | } 135 | document.querySelector("#console").insertAdjacentHTML("afterbegin", html); 136 | webview.scrollEnd(); 137 | }); 138 | -------------------------------------------------------------------------------- /assets/console-theme/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | } 4 | pre { 5 | padding: 0 10px; 6 | margin: 0; 7 | } 8 | #container{ 9 | width: 100%; 10 | } 11 | #console{ 12 | border: 5px solid var(--vscode-editor-background); 13 | background-color: var(--vscode-sideBar-background); 14 | box-sizing: border-box; 15 | min-height: 500px; 16 | padding: 5px; 17 | overflow-x: hidden; 18 | } 19 | #commandline{ 20 | border: 5px solid var(--vscode-editor-background); 21 | background-color: var(--vscode-sideBar-background); 22 | line-height: 24px; 23 | font-size: 16px; 24 | position: sticky; 25 | bottom: 0; 26 | width: 100%; 27 | box-sizing: border-box; 28 | color: var(--vscode-editor-foreground); 29 | } 30 | .cmsg { 31 | margin-bottom: 5px; 32 | } 33 | .cmsg pre { 34 | white-space: pre-wrap; 35 | word-wrap: break-word; 36 | } 37 | -------------------------------------------------------------------------------- /assets/default-theme/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * window.webview 是一个内置全局变量,封装了一些与宿主交互的方法 3 | * @type {import("../types").Webview} 4 | */ 5 | var webview; 6 | 7 | let me = webview.self_uin; 8 | let c2c = webview.c2c; 9 | let uin = webview.target_uin; 10 | let nick = webview.nickname; 11 | let facePath = webview.faces_path; 12 | 13 | /** 14 | * 群员列表 15 | * @type {Map} 16 | */ 17 | let members = new Map; 18 | 19 | /** 20 | * 群资料 21 | * @type {import("oicq").GroupInfo} 22 | */ 23 | let ginfo; 24 | 25 | // 监听消息和通知 26 | webview.on("message", (data) => { 27 | appendMsg(genUserMessage(data.detail)); 28 | }); 29 | webview.on("notice", (data) => { 30 | appendMsg(genSystemMessage(data.detail)); 31 | }); 32 | 33 | function appendMsg(msg) { 34 | if (document.querySelector(".content-left").scrollTop + document.querySelector(".content-left").offsetHeight + 100 > document.querySelector(".content-left").scrollHeight) { 35 | var flag = 1; 36 | } 37 | document.querySelector("#lite-chatbox").insertAdjacentHTML("beforeend", msg); 38 | if (flag) { 39 | document.querySelector(".content-left").scroll(0, document.querySelector(".content-left").scrollHeight); 40 | } 41 | } 42 | 43 | async function updateMemberList() { 44 | ginfo = (await webview.getGroupInfo(uin)).data; 45 | const arr = (await webview.getGroupMemberList(uin)).data; 46 | members = new Map; 47 | const element = document.querySelector(".group-members"); 48 | element.innerHTML = ""; 49 | let owner_html = ""; 50 | for (let v of arr) { 51 | members.set(v.user_id, v); 52 | const role = v.role === "owner" ? "🟡" : (v.role === "admin" ? "🟢" : ""); 53 | const html = `

${role + filterXss(v.card || v.nickname)}

`; 54 | if (v.role === "owner") { 55 | owner_html = html; 56 | continue; 57 | } 58 | element.insertAdjacentHTML(v.role === "member" ? "beforeend" : "afterbegin", html); 59 | } 60 | element.insertAdjacentHTML("afterbegin", owner_html); 61 | } 62 | 63 | function getChatHistory(message_id = "", count = 20) { 64 | webview.getChatHistory(message_id, count).then((data) => { 65 | let html = ""; 66 | let tmp = []; 67 | for (let msg of data.data) { 68 | if (msg.message_id !== message_id && !tmp.includes(msg.message_id)) { 69 | tmp.push(msg.message_id); 70 | html += genUserMessage(msg); 71 | } 72 | } 73 | if (!html) { 74 | return; 75 | } 76 | document.querySelector("#lite-chatbox").insertAdjacentHTML("afterbegin", html); 77 | if (message_id) { 78 | window.location.hash = "#" + message_id; 79 | } else { 80 | document.querySelector(".content-left").scroll(0, document.querySelector(".content-left").scrollHeight); 81 | } 82 | }); 83 | } 84 | 85 | let sending = false; 86 | const pastedImageBufferSize = 10_000_000; 87 | /** @type {{ placeholder: string, cqcode: string, url: string }[]} */ 88 | const pastedImageMappings = []; 89 | function sendMsg() { 90 | let message = `${document.querySelector("#content").value}`; 91 | if (sending || !message) { 92 | return; 93 | } 94 | sending = true; 95 | document.querySelector("#send").disabled = true; 96 | 97 | // 把粘贴的图片占位符重新转换为 CQ 码 98 | const splitted = [] 99 | let messageHtml = ''; 100 | while (true) { 101 | let begin = Infinity; 102 | /** @type {typeof pastedImageMappings[0]} */ 103 | let found; 104 | for (const x of pastedImageMappings) { 105 | const index = message.indexOf(x.placeholder); 106 | if (index != -1 && index < begin) { 107 | found = x; 108 | begin = index; 109 | } 110 | } 111 | 112 | if (begin === Infinity) { 113 | messageHtml += filterXss(message); 114 | splitted.push(message); 115 | break; 116 | } 117 | const before = message.slice(0, begin); 118 | 119 | splitted.push(before); 120 | splitted.push(found.cqcode); 121 | message = message.slice(begin + found.placeholder.length); 122 | 123 | messageHtml += filterXss(before); 124 | messageHtml += `粘贴的图片`; 125 | } 126 | // 真正的消息,已经把把图片占位符转换成了 CQ 码 127 | const realMessage = splitted.join(""); 128 | 129 | // 计算目前的空间占用,清理比较老的图片 130 | let currentSize = 0; 131 | let clearIndex = pastedImageMappings.length - 1; 132 | for (; clearIndex >= 0; --clearIndex) { 133 | const size = pastedImageMappings[clearIndex].cqcode.length / 4 * 3; 134 | currentSize += size; 135 | if (currentSize >= pastedImageBufferSize) { 136 | break; 137 | } 138 | } 139 | if (clearIndex > 0) { 140 | const removed = pastedImageMappings.splice(0, clearIndex); 141 | for (const { url } of removed) { 142 | URL.revokeObjectURL(url); 143 | } 144 | console.log(`Removed ${removed.length} items`); 145 | } 146 | 147 | webview.sendMsg(realMessage).then((data) => { 148 | if (data.retcode > 1) { 149 | let msg = data.error?.message; 150 | if (msg?.includes("禁言")) { 151 | if (ginfo.shutup_time_me * 1000 > Date.now()) { 152 | msg += " (至" + datetime(ginfo.shutup_time_me) + ")"; 153 | } else if (ginfo.shutup_time_whole) { 154 | msg += " (全员禁言)"; 155 | } 156 | } else if (data.retcode === 104) { 157 | msg = "断线了,发送失败"; 158 | } 159 | document.querySelector("#lite-chatbox").insertAdjacentHTML("beforeend", `
160 | Error: ${msg} 161 |
`); 162 | return; 163 | } 164 | if (c2c && data.data.message_id) { 165 | const html = `
166 | 167 | ${nick} ${timestamp()} 168 | ${messageHtml} 169 |
`; 170 | document.querySelector("#lite-chatbox").insertAdjacentHTML("beforeend", html); 171 | } 172 | document.querySelector("#content").value = ""; 173 | currentTextareaContent = ""; 174 | }).catch(() => { 175 | document.querySelector("#content").value = ""; 176 | currentTextareaContent = ""; 177 | }).finally(() => { 178 | sending = false; 179 | document.querySelector("#send").disabled = false; 180 | document.querySelector(".content-left").scroll(0, document.querySelector(".content-left").scrollHeight); 181 | }); 182 | } 183 | 184 | /** 185 | * 生成系统消息 186 | * @param {import("oicq").GroupNoticeEventData | import("oicq").FriendNoticeEventData} data 187 | */ 188 | function genSystemMessage(data) { 189 | let msg = ""; 190 | if (data.notice_type === "friend") { 191 | switch (data.sub_type) { 192 | case "poke": 193 | msg = `${data.operator_id} ${data.action} ${data.target_id} ${data.suffix}`; 194 | break; 195 | case "recall": 196 | msg = `有人想撤回 一条消息`; 197 | appendRecalledText(data.message_id); 198 | break; 199 | } 200 | } else if (data.notice_type === "group") { 201 | switch (data.sub_type) { 202 | case "recall": 203 | msg = `${genLabel(data.operator_id)} 撤回了 ${data.user_id === data.operator_id ? "自己" : genLabel(data.user_id)} 的一条消息`; 204 | appendRecalledText(data.message_id); 205 | break; 206 | case "increase": 207 | msg = `${filterXss(data.nickname)}(${data.user_id}) 加入了群聊`; 208 | updateMemberList(); 209 | break; 210 | case "decrease": 211 | if (data.dismiss) { 212 | msg = `该群已被解散`; 213 | break; 214 | } 215 | if (data.operator_id === data.user_id) { 216 | msg = `${genLabel(data.user_id)} 退出了群聊`; 217 | } else { 218 | msg = `${genLabel(data.operator_id)} 踢出了 ${genLabel(data.user_id)}`; 219 | } 220 | updateMemberList(); 221 | break; 222 | case "admin": 223 | msg = `${genLabel(data.user_id)} ${data.set ? "成为了" : "被取消了"}管理员`; 224 | updateMemberList(); 225 | break; 226 | case "transfer": 227 | msg = `${genLabel(data.operator_id)} 将群主转让给了 ${genLabel(data.user_id)}`; 228 | updateMemberList(); 229 | break; 230 | case "ban": 231 | if (data.user_id > 0) { 232 | msg = `${genLabel(data.operator_id)} 禁言 ${data.user_id === 80000000 ? "匿名用户(" + data.nickname + ")" : genLabel(data.user_id)} ${~~(data.duration/60)}分钟`; 233 | } else { 234 | msg = `${genLabel(data.operator_id)} ${data.duration > 0 ? "开启" : "关闭"}了全员禁言`; 235 | } 236 | updateMemberList(); 237 | break; 238 | case "poke": 239 | msg = `${genLabel(data.operator_id)} ${data.action} ${genLabel(data.user_id)} ${data.suffix}`; 240 | break; 241 | case "setting": 242 | if (data.group_name) { 243 | msg = `群名已变更为 ` + data.group_name; 244 | } 245 | break; 246 | } 247 | } 248 | if (!msg) { 249 | return ""; 250 | } 251 | return `
252 | ${msg} 253 |
`; 254 | } 255 | 256 | /** 257 | * 生成标签 258 | * @param {number} user_id 259 | */ 260 | function genLabel(user_id) { 261 | const member = members?.get(user_id); 262 | if (!member) { 263 | return user_id; 264 | } 265 | return `${filterXss(member.card ? member.card : member.nickname)}`; 266 | } 267 | 268 | /** 269 | * 转义message_id中的特殊字符 270 | * @param {string} message_id 271 | */ 272 | function filterMsgIdSelector(message_id) { 273 | return message_id.replace(/\//g, "\\/").replace(/\=/g, "\\=").replace(/\+/g, "\\+"); 274 | } 275 | 276 | /** 277 | * @param {string} message_id 278 | */ 279 | function appendRecalledText(message_id) { 280 | document.querySelector("a[id=" + filterMsgIdSelector(message_id) + "]+div span")?.append(" (已撤回)"); 281 | } 282 | 283 | /** 284 | * 生成一般消息 285 | * @param {import("oicq").PrivateMessageEventData | import("oicq").GroupMessageEventData} data 286 | */ 287 | function genUserMessage(data) { 288 | if (document.querySelector("#" + filterMsgIdSelector(data.message_id))) { 289 | return ""; 290 | } 291 | let title = ""; 292 | data.user_id = data.sender.user_id; 293 | 294 | if (data.anonymous) { 295 | data.sender.card = data.anonymous.name; 296 | title = `匿名`; 297 | } else { 298 | const role = members.get(data.user_id)?.role; 299 | if (role === "admin" || role === "owner") { 300 | title = `${role}`; 301 | } 302 | } 303 | return `
304 | 305 | 306 | ${c2c?"":'...'} 307 | ${title}${filterXss(data.sender.card ? data.sender.card : data.sender.nickname)} ${timestamp(data.time)} 308 | 309 | ${parseMessage(data.message)} 310 |
`; 311 | } 312 | 313 | const xssMap = { 314 | "&": "&", 315 | "\"": """, 316 | "<": "<", 317 | ">": ">", 318 | " ": " ", 319 | "\t": " ", 320 | }; 321 | 322 | /** 323 | * xss过滤 324 | * @param {string} str 325 | */ 326 | function filterXss(str) { 327 | str = str.replace(/[&"<>\t ]/g, (s) => { 328 | return xssMap[s]; 329 | }); 330 | str = str.replace(/\r\n/g, "
").replace(/\r/g, "
").replace(/\n/g, "
"); 331 | return str; 332 | } 333 | 334 | /** 335 | * 生成用户头像url 336 | * @param {number} user_id 337 | */ 338 | function genAvaterUrl(user_id) { 339 | return webview.getUserAvaterUrlSmall(user_id); 340 | } 341 | 342 | /** 343 | * 生成消息字符串 344 | * @param {import("oicq").MessageElem[]} message 345 | */ 346 | function parseMessage(message) { 347 | let msg = ""; 348 | for (let v of message) { 349 | switch (v.type) { 350 | case "text": 351 | msg += filterXss(v.data.text).replace(/(https?:\/\/[^\s]+)/g, '$1'); 352 | break; 353 | case "at": 354 | msg += `${filterXss(v.data.text)}`; 355 | break; 356 | case "face": 357 | if (v.data.id > 324) { 358 | msg += v.data.text || "[表情]"; 359 | } else { 360 | msg += ``; 361 | } 362 | break; 363 | case "sface": 364 | case "bface": 365 | if (v.data.text) { 366 | msg += "[" + filterXss(v.data.text) + "]"; 367 | } else { 368 | msg += "[表情]"; 369 | } 370 | break; 371 | case "image": 372 | case "flash": 373 | if (!c2c) { 374 | v.data.url = v.data.url.replace(/\/[0-9]+\//, "/0/").replace(/[0-9]+-/g, "0-"); 375 | } 376 | let split = v.data.file.split("-"); 377 | let width = parseInt(split[1]), height = parseInt(split[2]); 378 | msg += `${v.type === "image" ? "图片" : "闪照"}`; 379 | break; 380 | case "record": 381 | msg = `语音消息`; 382 | break; 383 | case "video": 384 | msg = `视频消息`; 385 | break; 386 | case "xml": 387 | const dom = new DOMParser().parseFromString(v.data.data, "text/xml"); 388 | if (dom.querySelector("msg")?.getAttribute("serviceID") === "35") { 389 | try { 390 | const resid = /resid="[^"]+"/.exec(v.data.data)[0].replace("resid=\"", "").replace("\"", ""); 391 | msg = `[合并转发]`; 392 | } catch { 393 | msg = `[嵌套转发]${filterXss(v.data.data)}`; 394 | } 395 | } else { 396 | if (dom.querySelector("msg")?.getAttribute("action") === "web") { //判断是否为链接分享 397 | const title = dom.querySelector("msg").getAttribute("brief"); 398 | const url = dom.querySelector("msg").getAttribute("url"); 399 | msg = `${filterXss(title)}
` + filterXss(dom.querySelector("summary")?.innerHTML); 400 | } else { 401 | msg = `[XML卡片消息]${filterXss(v.data.data)}`; 402 | } 403 | } 404 | break; 405 | case "json": 406 | try { 407 | const jsonObj = JSON.parse(v.data.data); 408 | if (jsonObj["app"] === "com.tencent.mannounce") { //判断是否为群公告 409 | const title = decodeURIComponent(escape(atob(jsonObj["meta"]["mannounce"]["title"]))); 410 | const content = decodeURIComponent(escape(atob(jsonObj["meta"]["mannounce"]["text"]))); 411 | msg = `${filterXss(title)}
${filterXss(content)}
`; 412 | } else { 413 | msg = `[JSON卡片消息]${filterXss(JSON.stringify(jsonObj, null, 4))}`; 414 | } 415 | } catch { } 416 | break; 417 | case "file": 418 | msg = `文件: ${filterXss(v.data.name)} (${v.data.size / 1e6}MB)`; 419 | break; 420 | case "reply": 421 | if (message[1]?.type === "at" && message[3]?.type === "at" && message[1]?.data.qq === message[3]?.data.qq) { 422 | message.splice(1, 2); 423 | } 424 | msg += `[回复]`; 425 | break; 426 | case "rps": 427 | msg += "[猜拳]"; 428 | break; 429 | case "dice": 430 | msg += "[骰子]"; 431 | break; 432 | case "shake": 433 | msg = "[窗口抖动]"; 434 | break; 435 | case "poke": 436 | msg = "[戳一戳]"; 437 | break; 438 | } 439 | } 440 | return msg; 441 | } 442 | 443 | /** 444 | * 加入at元素到输入框 445 | * @param {number|"all"} uid 446 | */ 447 | function addAt(uid) { 448 | if (c2c) { 449 | return; 450 | } 451 | const cqcode = `[CQ:at,qq=${uid}] `; 452 | addStr2Textarea(cqcode); 453 | } 454 | 455 | /** 456 | * 加入表情到输入框 457 | * @param {number} id 458 | */ 459 | function addFace(id) { 460 | const cqcode = `[CQ:face,id=${id}]`; 461 | addStr2Textarea(cqcode); 462 | } 463 | 464 | /** 465 | * 加入图片到输入框 466 | * @param {string} file 467 | */ 468 | function addImage(file) { 469 | const cqcode = `[CQ:image,file=${file},type=face]`; 470 | addStr2Textarea(cqcode); 471 | } 472 | 473 | function addStr2Textarea(str) { 474 | currentTextareaContent += str; 475 | document.querySelector("#content").value = currentTextareaContent; 476 | document.querySelector("#content").focus(); 477 | } 478 | 479 | function setTextareaText(str) { 480 | currentTextareaContent = str; 481 | document.querySelector("#content").value = currentTextareaContent; 482 | document.querySelector("#content").focus(); 483 | } 484 | 485 | function insertStr2Textarea(str) { 486 | const textArea = document.querySelector("#content"); 487 | if (textArea.selectionStart || textArea.selectionStart == '0') { 488 | const begin = textArea.selectionStart; 489 | const end = textArea.selectionEnd || textArea.selectionStart; 490 | setTextareaText(textArea.value.substring(0, begin) + str + textArea.value.substring(end)); 491 | textArea.selectionStart = textArea.selectionEnd = begin + str.length; 492 | } 493 | else { 494 | addStr2Textarea(str); 495 | } 496 | } 497 | 498 | let currentTextareaContent = ""; 499 | 500 | document.querySelector("body").insertAdjacentHTML("beforeend", `
501 |
502 | 双击加载历史消息 503 |
504 |
505 |
506 |
507 | 508 | 516 | 522 | 534 |
535 |
536 |
537 | 538 |
539 |
540 | 548 |
`); 549 | 550 | const idPreviewElement = document.querySelector("#img-preview"); 551 | const idShowStampBox = document.querySelector('#show-stamp-box'); 552 | const idShowFaceBox = document.querySelector('#show-face-box'); 553 | const idShowEmojiBox = document.querySelector('#show-emoji-box'); 554 | 555 | // add face to document 556 | let tmpFaceStep = 0; 557 | for (let i = 0; i <= 324; ++i) { 558 | if (i === 275 || (i > 247 && i < 260)) { 559 | continue; 560 | } 561 | ++tmpFaceStep; 562 | let html = ``; 563 | document.querySelector('.face-box').insertAdjacentHTML("beforeend", html); 564 | } 565 | document.querySelector("body").addEventListener("click", (e) => { 566 | document.querySelector('.face-box').style.display = 'none'; 567 | document.querySelector('.emoji-box').style.display = 'none'; 568 | document.querySelector('.stamp-box').style.display = 'none'; 569 | document.querySelector('.menu-msg').style.display = 'none'; 570 | document.querySelector('.menu-member').style.display = 'none'; 571 | if (e.target === idShowStampBox) { 572 | document.querySelector('.stamp-box').style.display = 'block'; 573 | if (!document.querySelector('.stamp-box img')) { 574 | // add stamp to document 575 | webview.getRoamingStamp().then((data) => { 576 | if (data.retcode === 0) { 577 | let tmpStampStep = 0; 578 | for (let i = data.data.length - 1; i >= 0; --i) { 579 | ++tmpStampStep; 580 | const url = data.data[i]; 581 | let html = `` + (tmpStampStep % 6 === 0 ? "
" : ""); 582 | document.querySelector('.stamp-box').insertAdjacentHTML("beforeend", html); 583 | } 584 | } 585 | }); 586 | } 587 | } else if (e.target === idShowFaceBox) { 588 | document.querySelector('.face-box').style.display = 'block'; 589 | } else if (e.target === idShowEmojiBox) { 590 | document.querySelector('.emoji-box').style.display = 'block'; 591 | } else if (e.target.classList.contains("operation")) { 592 | const msgid = e.target.parentNode.parentNode.previousElementSibling.id; 593 | document.querySelector('.menu-msg').style.left = e.target.getBoundingClientRect().x + 12 + "px"; 594 | document.querySelector('.menu-msg').style.top = e.target.getBoundingClientRect().y + "px"; 595 | document.querySelector('.menu-msg').style.display = 'block'; 596 | document.querySelector('.menu-msg .menu-msg-at').onclick = e.target.parentNode.ondblclick; 597 | document.querySelector('.menu-msg .menu-msg-reply').onclick = () => { 598 | addStr2Textarea(`[CQ:reply,id=${msgid}]`); 599 | e.target.parentNode.ondblclick(); 600 | }; 601 | document.querySelector('.menu-msg .menu-msg-recall').onclick = () => { 602 | showModalDialog("确定撤回此消息?", () => { 603 | webview.deleteMsg(msgid); 604 | }); 605 | }; 606 | const uid = Number(e.target.parentNode.attributes.uid.value); 607 | const member = members.get(uid); 608 | const label = filterXss(member?.card || member?.nickname || "未知用户") + "(" + uid + ")"; 609 | document.querySelector('.menu-msg .menu-msg-mute').onclick = () => { 610 | showModalDialog(`禁言以下成员 分钟
` + label, () => { 611 | const duration = document.querySelector("#mute-minutes").value; 612 | if (duration >= 0) { 613 | webview.setGroupBan(webview.target_uin, uid, Number(duration) * 60); 614 | } 615 | }); 616 | }; 617 | document.querySelector('.menu-msg .menu-msg-kick').onclick = () => { 618 | showModalDialog(`确定要删除以下成员:
` + label, () => { 619 | webview.setGroupKick(webview.target_uin, uid); 620 | }); 621 | }; 622 | document.querySelector('.menu-msg .menu-msg-poke').onclick = () => { 623 | webview.sendGroupPoke(webview.target_uin, uid); 624 | }; 625 | } else if (e.target.classList.contains("group-member")) { 626 | document.querySelector('.menu-member').style.left = e.target.getBoundingClientRect().x + 50 + "px"; 627 | document.querySelector('.menu-member').style.top = e.target.getBoundingClientRect().y + 10 + "px"; 628 | document.querySelector('.menu-member').style.display = 'block'; 629 | const uid = Number(e.target.attributes.uid.value); 630 | const member = members.get(uid); 631 | const label = filterXss(member?.card || member?.nickname || "未知用户") + "(" + uid + ")"; 632 | document.querySelector('.menu-member .menu-member-poke').onclick = () => { 633 | webview.sendGroupPoke(webview.target_uin, uid); 634 | }; 635 | document.querySelector('.menu-member .menu-member-at').onclick = () => { 636 | addAt(uid); 637 | }; 638 | document.querySelector('.menu-member .menu-member-mute').onclick = () => { 639 | showModalDialog(`禁言以下成员 分钟
` + label, () => { 640 | const duration = document.querySelector("#mute-minutes").value; 641 | if (duration >= 0) { 642 | webview.setGroupBan(webview.target_uin, uid, Number(duration) * 60); 643 | } 644 | }); 645 | }; 646 | document.querySelector('.menu-member .menu-member-kick').onclick = () => { 647 | showModalDialog(`确定要删除以下成员:
` + label, () => { 648 | webview.setGroupKick(webview.target_uin, uid); 649 | }); 650 | }; 651 | document.querySelector('.menu-member .menu-member-admin1').onclick = () => { 652 | webview.setGroupAdmin(webview.target_uin, uid, true); 653 | }; 654 | document.querySelector('.menu-member .menu-member-admin0').onclick = () => { 655 | webview.setGroupAdmin(webview.target_uin, uid, false); 656 | }; 657 | } 658 | }); 659 | document.querySelector("#insert-pic").addEventListener("click", () => { 660 | const cqcode = `[CQ:image,file=替换为本地图片或网络URL路径]`; 661 | addStr2Textarea(cqcode); 662 | }); 663 | 664 | let tmpEmojiStep = 0; 665 | function addEmoji2Box(from, to) { 666 | for (let i = from; i <= to; ++i) { 667 | ++tmpEmojiStep; 668 | let str = String.fromCodePoint(i); 669 | let html = `` + str + ""; 670 | document.querySelector('.emoji-box').insertAdjacentHTML("beforeend", html); 671 | } 672 | } 673 | addEmoji2Box(0x1F600, 0x1F64F); 674 | addEmoji2Box(0x1F90D, 0x1F945); 675 | addEmoji2Box(0x1F400, 0x1F4FF); 676 | addEmoji2Box(0x1F300, 0x1F320); 677 | addEmoji2Box(0x1F32D, 0x1F394); 678 | addEmoji2Box(0x1F3A0, 0x1F3FA); 679 | addEmoji2Box(0x1F680, 0x1F6C5); 680 | addEmoji2Box(0x1F004, 0x1F004); 681 | 682 | /** 683 | * 图片预览 684 | * @param {Element} obj 685 | */ 686 | function previewImage(obj, width, height) { 687 | const url = obj.href ?? obj.src.replace("100", "640"); 688 | if (width > 0 && width <= 200) { 689 | width = width + "px"; 690 | height = "auto"; 691 | } else if (height > 0 && height <= 200) { 692 | width = "auto"; 693 | height = height + "px"; 694 | } else if (height > 200 && width > 200) { 695 | if (width >= height) { 696 | width = "auto"; 697 | height = "200px"; 698 | } else { 699 | width = "200px"; 700 | height = "auto"; 701 | } 702 | } else { 703 | width = "200px"; 704 | height = "auto"; 705 | } 706 | idPreviewElement.style.width = width; 707 | idPreviewElement.style.height = height; 708 | let left = obj.getBoundingClientRect().x + 20; 709 | if (left + 150 > window.innerWidth) { 710 | left -= 200; 711 | } 712 | let top = obj.getBoundingClientRect().y - 5; 713 | idPreviewElement.src = url; 714 | idPreviewElement.style.left = left + "px"; 715 | idPreviewElement.style.top = top + "px"; 716 | idPreviewElement.style.display = "block"; 717 | obj.onmouseleave = () => idPreviewElement.style.display = "none"; 718 | } 719 | 720 | // Ctrl+Enter 721 | window.onkeydown = function (event) { 722 | if (event.ctrlKey && event.keyCode === 13) { 723 | sendMsg(); 724 | } 725 | }; 726 | 727 | //滚动到顶部加载消息 728 | document.querySelector(".content-left").onscroll = function () { 729 | if (document.querySelector(".content-left").scrollTop === 0) { 730 | getChatHistory(document.querySelector(".msgid")?.attributes.id.value ?? ""); 731 | } 732 | }; 733 | 734 | //表情、图片拖动 735 | document.querySelector("#content").oninput = function () { 736 | const content = this.value; 737 | const diff = content.substr(currentTextareaContent.length); 738 | if (diff.startsWith(facePath)) { 739 | const faceId = diff.substr(facePath.length).split(".")[0]; 740 | const cqcode = `[CQ:face,id=${faceId}]`; 741 | addStr2Textarea(cqcode); 742 | } else if (diff.endsWith("&vscodeDragFlag=1")) { 743 | const file = new URL(diff).searchParams.get("file"); 744 | const cqcode = `[CQ:image,file=${file},type=face]`; 745 | addStr2Textarea(cqcode); 746 | } else { 747 | currentTextareaContent = content; 748 | } 749 | }; 750 | 751 | // 粘贴图片 752 | document.querySelector("#content").addEventListener("paste", async ev => { 753 | /** @type {DataTransfer} */ 754 | const clipboardData = (ev.clipboardData || ev.originalEvent.clipboardData); 755 | const pasted = await Promise.all(Array.from(clipboardData.items).map(item => { 756 | if (item.kind !== "file") { 757 | // 处理富文本会比较麻烦,交给 textarea 自己去处理吧( 758 | // 可是,这样其实有个问题,假如同时复制了交错的文字与图片 759 | // 那么顺序将会被打乱 - 首先是 textarea 自己粘贴的文字,之后才是图片 760 | // 该怎么办才好呀 qwq 761 | return Promise.resolve(''); 762 | } 763 | if (!item.type.startsWith("image/")) { 764 | return Promise.resolve(`(暂不支持的文件类型:${item.type})`); 765 | } 766 | 767 | return new Promise((resolve, reject) => { 768 | const blob = item.getAsFile(); 769 | const url = URL.createObjectURL(blob); 770 | 771 | const reader = new FileReader(); 772 | reader.onload = () => { 773 | const base64 = reader.result.split(",")[1]; 774 | const cqcode = `[CQ:image,file=base64://${base64}]`; 775 | const placeholder = `[粘贴的图片 ${url}]`; 776 | pastedImageMappings.push({ placeholder, cqcode, url }); 777 | resolve(placeholder); 778 | } 779 | reader.onerror = reject; 780 | reader.readAsDataURL(blob) 781 | }) 782 | })) 783 | const text = pasted.join(""); 784 | insertStr2Textarea(text); 785 | }) 786 | 787 | function timestamp(unixstamp) { 788 | return webview.timestamp(unixstamp); 789 | } 790 | function datetime(unixstamp) { 791 | return webview.datetime(unixstamp); 792 | } 793 | 794 | function showModalDialog(title, cb) { 795 | document.querySelector(".modal-title").innerHTML = title; 796 | document.querySelector(".modal-dialog").style.display = "block"; 797 | document.querySelector(".modal-dialog").style.top = window.innerHeight / 2 - 50 + "px"; 798 | document.querySelector(".modal-dialog").style.left = window.innerWidth / 2 - 100 + "px"; 799 | document.querySelector(".modal-confirm").onclick = cb; 800 | } 801 | function closeModalDialog() { 802 | document.querySelector(".modal-dialog").style.display = "none"; 803 | } 804 | document.querySelector(".modal-confirm").addEventListener("click", closeModalDialog); 805 | 806 | function triggerRightBar() { 807 | if (c2c) { 808 | return; 809 | } 810 | if (document.querySelector(".content-right").style.display === "block") { 811 | document.querySelector(".content-right").style.display = "none"; 812 | } else { 813 | document.querySelector(".content-right").style.display = "block"; 814 | } 815 | } 816 | 817 | function triggerForwardMsg(obj) { 818 | const resid = obj.id; 819 | const elememt = obj.nextElementSibling; 820 | if (elememt.style.display === "block") { 821 | elememt.style.display = "none"; 822 | } else { 823 | elememt.style.display = "block"; 824 | } 825 | if (elememt.innerHTML === "" || elememt.innerHTML === "加载失败") { 826 | elememt.innerHTML = "..."; 827 | webview.getForwardMsg(resid).then(data=>{ 828 | let html = ""; 829 | for (let v of data.data) { 830 | html += `

👤${filterXss(v.nickname)}(${v.user_id}) ${datetime(v.time)}

${parseMessage(v.message)}`; 831 | } 832 | if (!html) { 833 | html = "加载失败"; 834 | } 835 | elememt.innerHTML = html; 836 | }); 837 | } 838 | } 839 | 840 | //init 841 | (async()=>{ 842 | if (!c2c) { 843 | //加载群资料、群员列表 844 | await updateMemberList(); 845 | } 846 | //加载历史消息 847 | getChatHistory(); 848 | })(); 849 | -------------------------------------------------------------------------------- /assets/default-theme/style.css: -------------------------------------------------------------------------------- 1 | /*! LiteWebChat_Frame v2.1.0 | MorFans Lab(c) 2017, BY:SuperPaxxs & Haswikr | https://lab.morfans.cn / Released under the LGPL License */ 2 | * { 3 | font-family: Helvetica,"PingFang SC","Microsoft YaHei",sans-serif; 4 | } 5 | .lite-chatbox{ 6 | padding:0; 7 | width:100%; 8 | position:relative; 9 | overflow-y:auto; 10 | overflow-x:hidden; 11 | font:18px 12 | } 13 | .lite-chatbox .cmsg{ 14 | position:relative; 15 | margin:4px 7px; 16 | min-height:50px; 17 | border:0 18 | } 19 | .lite-chatbox .cright{ 20 | text-align:right; 21 | margin-left:64px 22 | } 23 | .lite-chatbox .cleft{ 24 | text-align:left; 25 | margin-right:64px 26 | } 27 | .lite-chatbox img.headIcon{ 28 | width:34px; 29 | height:34px; 30 | top:9px; 31 | position:absolute; 32 | } 33 | .lite-chatbox .name{ 34 | color:var(--vscode-input-foreground); 35 | font-size:12px; 36 | display:block; 37 | line-height:18px 38 | } 39 | .lite-chatbox .name .htitle{ 40 | display:inline-block; 41 | padding:0 3px; 42 | background-color:#aaa; 43 | color:#fff; 44 | -moz-border-radius:4px; 45 | -webkit-border-radius:4px; 46 | border-radius:4px; 47 | margin-right:4px; 48 | font-size:11px; 49 | overflow:hidden; 50 | text-overflow:ellipsis; 51 | white-space:nowrap; 52 | vertical-align:middle; 53 | max-width:50px 54 | } 55 | .lite-chatbox .content{ 56 | word-break:break-all; 57 | word-wrap:break-word; 58 | text-align:left; 59 | position:relative; 60 | display:inline-block; 61 | font-size:15px; 62 | padding:10px 15px; 63 | line-height:20px; 64 | min-width:9px; 65 | min-height:18px 66 | } 67 | .lite-chatbox .content img{ 68 | width:100%; 69 | height:auto 70 | } 71 | .lite-chatbox .content a{ 72 | color:var(--vscode-editorLink-activeForeground); 73 | margin:0 2px; 74 | cursor:hand 75 | } 76 | .lite-chatbox .tips{ 77 | margin:12px; 78 | text-align:center; 79 | font-size:12px 80 | } 81 | .lite-chatbox .tips span{ 82 | display:inline-block; 83 | padding:4px; 84 | background-color:#aaa; 85 | color:#fff; 86 | -moz-border-radius:6px; 87 | -webkit-border-radius:6px; 88 | border-radius:6px 89 | } 90 | .lite-chatbox img.radius{ 91 | -moz-border-radius:20%; 92 | -webkit-border-radius:20%; 93 | border-radius:20% 94 | } 95 | .lite-chatbox .cright img.headIcon{ 96 | right:0 97 | } 98 | .lite-chatbox .cleft img.headIcon{ 99 | left:0 100 | } 101 | .lite-chatbox .cright .name{ 102 | margin:0 48px 2px 0 103 | } 104 | .lite-chatbox .cleft .name{ 105 | margin:0 0 2px 48px 106 | } 107 | .lite-chatbox .cright .content{ 108 | margin:0 48px 0 0; 109 | -webkit-border-radius:20px 0 20px 20px; 110 | border: 2px solid var(--vscode-quickInputTitle-background); 111 | border-radius:20px 0 20px 20px; 112 | /* color:#aaa; */ 113 | background:var(--vscode-sideBar-background); 114 | } 115 | .lite-chatbox .cleft .content{ 116 | margin:0 0 0 48px; 117 | -webkit-border-radius:0 20px 20px 20px; 118 | border-radius:0 20px 20px 20px; 119 | /* color:#aaa; */ 120 | border: 2px solid var(--vscode-quickInputTitle-background); 121 | background: var(--vscode-sideBar-background); 122 | } 123 | .lite-chatbox .cright .content:after{ 124 | right:-12px; 125 | top:8px 126 | } 127 | .lite-chatbox .cleft .content:after{ 128 | left:-12px; 129 | top:8px 130 | } 131 | .lite-chatbox .tips .tips-primary{ 132 | background-color:#3986c8 133 | } 134 | .lite-chatbox .tips .tips-success{ 135 | background-color:#49b649 136 | } 137 | .lite-chatbox .tips .tips-info{ 138 | background-color:#5bb6d1 139 | } 140 | .lite-chatbox .tips .tips-warning{ 141 | background-color:#eea948 142 | } 143 | .lite-chatbox .tips .tips-danger{ 144 | background-color:#e24d48 145 | } 146 | .lite-chatbox .name .admin{ 147 | background-color:#72D6A0 148 | } 149 | .lite-chatbox .name .owner{ 150 | background-color:#F2BF25 151 | } 152 | 153 | html,body{ 154 | height: 100%; 155 | margin: 0; 156 | padding: 0; 157 | } 158 | body { 159 | display: flex; 160 | } 161 | .lite-chatbox .name b.operation { 162 | cursor: pointer; 163 | background-color: var(--vscode-editor-background); 164 | padding: 0 2px; 165 | color: var(--vscode-editor-foreground); 166 | } 167 | .lite-chatbox .name b.operation:hover { 168 | background-color: var(--vscode-focusBorder); 169 | } 170 | .lite-chatbox .content img { 171 | width: auto; 172 | vertical-align: bottom; 173 | } 174 | .lite-chatbox .content img.face { 175 | width: 28px; 176 | height: 28px; 177 | } 178 | .lite-chatbox .content a { 179 | text-decoration: none; 180 | } 181 | .lite-chatbox .cmsg { 182 | margin: 10px 15px; 183 | } 184 | .lite-chatbox .cleft .content { 185 | border-radius: 0 10px 10px 10px; 186 | font-size: 14px; 187 | padding: 8px 15px; 188 | } 189 | 190 | .lite-chatbox .cright .content { 191 | border-radius: 10px 0px 10px 10px; 192 | font-size: 14px; 193 | padding: 8px 15px; 194 | } 195 | 196 | #img-preview { 197 | position: fixed; 198 | display: none; 199 | width: 200px; 200 | border: var(--vscode-editor-foreground) 1px solid; 201 | } 202 | #content { 203 | width: 100%; 204 | resize: none; 205 | box-sizing: border-box; 206 | background-color: var(--vscode-editor-background); 207 | color: var(--vscode-editor-foreground); 208 | font-size: 16px; 209 | padding: 10px; 210 | } 211 | button#send { 212 | margin-right: 10px; 213 | padding-top: 0.5em; 214 | padding-bottom: 0.5em; 215 | padding-left: 1.5em; 216 | padding-right: 1.5em; 217 | border-radius: 8px; 218 | -webkit-border-radius: 8px; 219 | background:var(--vscode-editor-background); 220 | color: var(--vscode-editor-foreground); 221 | font-weight: bold; 222 | font-size: 16px; 223 | } 224 | #to-bottom { 225 | position: absolute; 226 | bottom: 0; 227 | right: 0; 228 | margin: 10px; 229 | cursor: pointer; 230 | } 231 | #footer { 232 | position: sticky; 233 | width: 100%; 234 | bottom: 0; 235 | background-color: var(--vscode-editor-background); 236 | } 237 | .box { 238 | height: 240px; 239 | width: 480px; 240 | overflow-y: auto; 241 | background-color: var(--vscode-editor-background); 242 | max-height: 240px; 243 | display: none; 244 | position: absolute; 245 | bottom: 40px; 246 | left: 180px; 247 | padding: 5px; 248 | border: solid 1px var(--vscode-editor-foreground); 249 | font-size: 20px; 250 | } 251 | .stamp-box img { 252 | margin:5px; 253 | cursor:pointer; 254 | max-width:68px; 255 | max-height:68px; 256 | min-width:68px; 257 | vertical-align: middle; 258 | } 259 | .insert-button { 260 | cursor: pointer; 261 | font-size: 18px; 262 | padding: 2px; 263 | } 264 | 265 | .modal-dialog { 266 | position: fixed; 267 | z-index: 9999; 268 | display: none; 269 | width: 200px; 270 | height: auto; 271 | max-width: 200px; 272 | border: solid 1px var(--vscode-input-foreground); 273 | background-color: var(--vscode-sideBar-background); 274 | } 275 | .modal-title { 276 | padding: 10px 20px 0px 20px; 277 | line-height: 20px; 278 | } 279 | .modal-button { 280 | position: relative; 281 | bottom: 0; 282 | padding: 15px 50px; 283 | } 284 | 285 | .menu-msg { 286 | position: fixed; 287 | display: none; 288 | z-index: 100; 289 | border: solid 1px var(--vscode-input-foreground); 290 | width: 100px; 291 | opacity: 0.85; 292 | } 293 | .menu-msg hr { 294 | background-color: var(--vscode-sideBar-background); 295 | height: 2px; 296 | border-top:ridge 1px var(--vscode-input-foreground); 297 | border-bottom: 0; 298 | border-left: 0; 299 | border-right: 0; 300 | margin: 0; 301 | } 302 | .menu-msg div { 303 | cursor: pointer; 304 | background-color: var(--vscode-sideBar-background); 305 | padding: 2px; 306 | } 307 | .menu-msg div:hover { 308 | background-color: var(--vscode-input-foreground); 309 | color: var(--vscode-editor-background); 310 | } 311 | 312 | .content-left { 313 | overflow-y: auto; 314 | flex: 1; 315 | } 316 | 317 | .content-right { 318 | width: 200px; 319 | border-left: solid 1px var(--vscode-input-foreground); 320 | overflow-y: auto; 321 | overflow-x: hidden; 322 | display: none; 323 | } 324 | .group-info { 325 | height: 100px; 326 | position: sticky; 327 | top: 0; 328 | background-color: var(--vscode-editor-background); 329 | border-bottom: solid 1px var(--vscode-input-foreground); 330 | } 331 | .group-info img { 332 | width: 80px; 333 | height: 80px; 334 | border: 1px solid #c5d4c4; 335 | border-radius: 10%; 336 | margin: 10px 55px; 337 | } 338 | .group-members { 339 | padding: 5px 0; 340 | margin-bottom: 150px; 341 | } 342 | .group-member { 343 | cursor: pointer; 344 | padding: 5px 10px; 345 | margin: 0; 346 | width: 190px; 347 | white-space: nowrap; 348 | text-overflow: ellipsis; 349 | overflow: hidden; 350 | } 351 | .group-member:hover { 352 | background-color: var(--vscode-sideBar-background); 353 | } 354 | 355 | .menu-member { 356 | position: fixed; 357 | display: none; 358 | z-index: 100; 359 | border: solid 1px var(--vscode-input-foreground); 360 | width: 100px; 361 | opacity: 0.85; 362 | } 363 | .menu-member div { 364 | cursor: pointer; 365 | background-color: var(--vscode-sideBar-background); 366 | padding: 2px; 367 | } 368 | .menu-member div:hover { 369 | background-color: var(--vscode-input-foreground); 370 | color: var(--vscode-editor-background); 371 | } 372 | 373 | .msg-forward p { 374 | color: var(--vscode-input-foreground); 375 | } 376 | 377 | .jsonMsgTitle{ 378 | font-weight: bold; 379 | font-size: 20px; 380 | } 381 | 382 | /* .xmlMsgWrapper { 383 | display: table; 384 | } 385 | 386 | .xmlMsgContent { 387 | vertical-align: middle; 388 | list-style: none; 389 | } 390 | 391 | .xmlMsgTitle { 392 | font-size: 20px; 393 | font-weight: bold; 394 | } 395 | 396 | .xmlMsgWrapper div{ 397 | display: table-cell; 398 | vertical-align: middle; 399 | text-align: left; 400 | } */ 401 | -------------------------------------------------------------------------------- /assets/faces/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/0.png -------------------------------------------------------------------------------- /assets/faces/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/1.png -------------------------------------------------------------------------------- /assets/faces/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/10.png -------------------------------------------------------------------------------- /assets/faces/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/100.png -------------------------------------------------------------------------------- /assets/faces/101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/101.png -------------------------------------------------------------------------------- /assets/faces/102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/102.png -------------------------------------------------------------------------------- /assets/faces/103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/103.png -------------------------------------------------------------------------------- /assets/faces/104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/104.png -------------------------------------------------------------------------------- /assets/faces/105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/105.png -------------------------------------------------------------------------------- /assets/faces/106.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/106.png -------------------------------------------------------------------------------- /assets/faces/107.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/107.png -------------------------------------------------------------------------------- /assets/faces/108.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/108.png -------------------------------------------------------------------------------- /assets/faces/109.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/109.png -------------------------------------------------------------------------------- /assets/faces/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/11.png -------------------------------------------------------------------------------- /assets/faces/110.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/110.png -------------------------------------------------------------------------------- /assets/faces/111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/111.png -------------------------------------------------------------------------------- /assets/faces/112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/112.png -------------------------------------------------------------------------------- /assets/faces/113.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/113.png -------------------------------------------------------------------------------- /assets/faces/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/114.png -------------------------------------------------------------------------------- /assets/faces/115.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/115.png -------------------------------------------------------------------------------- /assets/faces/116.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/116.png -------------------------------------------------------------------------------- /assets/faces/117.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/117.png -------------------------------------------------------------------------------- /assets/faces/118.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/118.png -------------------------------------------------------------------------------- /assets/faces/119.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/119.png -------------------------------------------------------------------------------- /assets/faces/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/12.png -------------------------------------------------------------------------------- /assets/faces/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/120.png -------------------------------------------------------------------------------- /assets/faces/121.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/121.png -------------------------------------------------------------------------------- /assets/faces/122.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/122.png -------------------------------------------------------------------------------- /assets/faces/123.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/123.png -------------------------------------------------------------------------------- /assets/faces/124.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/124.png -------------------------------------------------------------------------------- /assets/faces/125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/125.png -------------------------------------------------------------------------------- /assets/faces/126.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/126.png -------------------------------------------------------------------------------- /assets/faces/127.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/127.png -------------------------------------------------------------------------------- /assets/faces/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/128.png -------------------------------------------------------------------------------- /assets/faces/129.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/129.png -------------------------------------------------------------------------------- /assets/faces/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/13.png -------------------------------------------------------------------------------- /assets/faces/130.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/130.png -------------------------------------------------------------------------------- /assets/faces/131.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/131.png -------------------------------------------------------------------------------- /assets/faces/132.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/132.png -------------------------------------------------------------------------------- /assets/faces/133.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/133.png -------------------------------------------------------------------------------- /assets/faces/134.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/134.png -------------------------------------------------------------------------------- /assets/faces/135.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/135.png -------------------------------------------------------------------------------- /assets/faces/136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/136.png -------------------------------------------------------------------------------- /assets/faces/137.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/137.png -------------------------------------------------------------------------------- /assets/faces/138.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/138.png -------------------------------------------------------------------------------- /assets/faces/139.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/139.png -------------------------------------------------------------------------------- /assets/faces/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/14.png -------------------------------------------------------------------------------- /assets/faces/140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/140.png -------------------------------------------------------------------------------- /assets/faces/141.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/141.png -------------------------------------------------------------------------------- /assets/faces/142.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/142.png -------------------------------------------------------------------------------- /assets/faces/143.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/143.png -------------------------------------------------------------------------------- /assets/faces/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/144.png -------------------------------------------------------------------------------- /assets/faces/145.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/145.png -------------------------------------------------------------------------------- /assets/faces/146.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/146.png -------------------------------------------------------------------------------- /assets/faces/147.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/147.png -------------------------------------------------------------------------------- /assets/faces/148.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/148.png -------------------------------------------------------------------------------- /assets/faces/149.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/149.png -------------------------------------------------------------------------------- /assets/faces/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/15.png -------------------------------------------------------------------------------- /assets/faces/150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/150.png -------------------------------------------------------------------------------- /assets/faces/151.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/151.png -------------------------------------------------------------------------------- /assets/faces/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/152.png -------------------------------------------------------------------------------- /assets/faces/153.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/153.png -------------------------------------------------------------------------------- /assets/faces/154.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/154.png -------------------------------------------------------------------------------- /assets/faces/155.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/155.png -------------------------------------------------------------------------------- /assets/faces/156.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/156.png -------------------------------------------------------------------------------- /assets/faces/157.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/157.png -------------------------------------------------------------------------------- /assets/faces/158.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/158.png -------------------------------------------------------------------------------- /assets/faces/159.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/159.png -------------------------------------------------------------------------------- /assets/faces/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/16.png -------------------------------------------------------------------------------- /assets/faces/160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/160.png -------------------------------------------------------------------------------- /assets/faces/161.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/161.png -------------------------------------------------------------------------------- /assets/faces/162.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/162.png -------------------------------------------------------------------------------- /assets/faces/163.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/163.png -------------------------------------------------------------------------------- /assets/faces/164.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/164.png -------------------------------------------------------------------------------- /assets/faces/165.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/165.png -------------------------------------------------------------------------------- /assets/faces/166.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/166.png -------------------------------------------------------------------------------- /assets/faces/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/167.png -------------------------------------------------------------------------------- /assets/faces/168.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/168.png -------------------------------------------------------------------------------- /assets/faces/169.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/169.png -------------------------------------------------------------------------------- /assets/faces/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/17.png -------------------------------------------------------------------------------- /assets/faces/170.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/170.png -------------------------------------------------------------------------------- /assets/faces/171.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/171.png -------------------------------------------------------------------------------- /assets/faces/172.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/172.png -------------------------------------------------------------------------------- /assets/faces/173.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/173.png -------------------------------------------------------------------------------- /assets/faces/174.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/174.png -------------------------------------------------------------------------------- /assets/faces/175.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/175.png -------------------------------------------------------------------------------- /assets/faces/176.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/176.png -------------------------------------------------------------------------------- /assets/faces/177.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/177.png -------------------------------------------------------------------------------- /assets/faces/178.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/178.png -------------------------------------------------------------------------------- /assets/faces/179.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/179.png -------------------------------------------------------------------------------- /assets/faces/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/18.png -------------------------------------------------------------------------------- /assets/faces/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/180.png -------------------------------------------------------------------------------- /assets/faces/181.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/181.png -------------------------------------------------------------------------------- /assets/faces/182.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/182.png -------------------------------------------------------------------------------- /assets/faces/183.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/183.png -------------------------------------------------------------------------------- /assets/faces/184.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/184.png -------------------------------------------------------------------------------- /assets/faces/185.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/185.png -------------------------------------------------------------------------------- /assets/faces/186.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/186.png -------------------------------------------------------------------------------- /assets/faces/187.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/187.png -------------------------------------------------------------------------------- /assets/faces/188.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/188.png -------------------------------------------------------------------------------- /assets/faces/189.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/189.png -------------------------------------------------------------------------------- /assets/faces/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/19.png -------------------------------------------------------------------------------- /assets/faces/190.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/190.png -------------------------------------------------------------------------------- /assets/faces/191.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/191.png -------------------------------------------------------------------------------- /assets/faces/192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/192.png -------------------------------------------------------------------------------- /assets/faces/193.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/193.png -------------------------------------------------------------------------------- /assets/faces/194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/194.png -------------------------------------------------------------------------------- /assets/faces/195.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/195.png -------------------------------------------------------------------------------- /assets/faces/196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/196.png -------------------------------------------------------------------------------- /assets/faces/197.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/197.png -------------------------------------------------------------------------------- /assets/faces/198.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/198.png -------------------------------------------------------------------------------- /assets/faces/199.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/199.png -------------------------------------------------------------------------------- /assets/faces/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/2.png -------------------------------------------------------------------------------- /assets/faces/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/20.png -------------------------------------------------------------------------------- /assets/faces/200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/200.png -------------------------------------------------------------------------------- /assets/faces/201.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/201.png -------------------------------------------------------------------------------- /assets/faces/202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/202.png -------------------------------------------------------------------------------- /assets/faces/203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/203.png -------------------------------------------------------------------------------- /assets/faces/204.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/204.png -------------------------------------------------------------------------------- /assets/faces/205.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/205.png -------------------------------------------------------------------------------- /assets/faces/206.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/206.png -------------------------------------------------------------------------------- /assets/faces/207.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/207.png -------------------------------------------------------------------------------- /assets/faces/208.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/208.png -------------------------------------------------------------------------------- /assets/faces/209.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/209.png -------------------------------------------------------------------------------- /assets/faces/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/21.png -------------------------------------------------------------------------------- /assets/faces/210.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/210.png -------------------------------------------------------------------------------- /assets/faces/211.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/211.png -------------------------------------------------------------------------------- /assets/faces/212.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/212.png -------------------------------------------------------------------------------- /assets/faces/213.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/213.png -------------------------------------------------------------------------------- /assets/faces/214.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/214.png -------------------------------------------------------------------------------- /assets/faces/215.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/215.png -------------------------------------------------------------------------------- /assets/faces/216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/216.png -------------------------------------------------------------------------------- /assets/faces/217.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/217.png -------------------------------------------------------------------------------- /assets/faces/218.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/218.png -------------------------------------------------------------------------------- /assets/faces/219.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/219.png -------------------------------------------------------------------------------- /assets/faces/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/22.png -------------------------------------------------------------------------------- /assets/faces/220.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/220.png -------------------------------------------------------------------------------- /assets/faces/221.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/221.png -------------------------------------------------------------------------------- /assets/faces/222.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/222.png -------------------------------------------------------------------------------- /assets/faces/223.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/223.png -------------------------------------------------------------------------------- /assets/faces/224.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/224.png -------------------------------------------------------------------------------- /assets/faces/225.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/225.png -------------------------------------------------------------------------------- /assets/faces/226.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/226.png -------------------------------------------------------------------------------- /assets/faces/227.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/227.png -------------------------------------------------------------------------------- /assets/faces/228.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/228.png -------------------------------------------------------------------------------- /assets/faces/229.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/229.png -------------------------------------------------------------------------------- /assets/faces/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/23.png -------------------------------------------------------------------------------- /assets/faces/230.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/230.png -------------------------------------------------------------------------------- /assets/faces/231.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/231.png -------------------------------------------------------------------------------- /assets/faces/232.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/232.png -------------------------------------------------------------------------------- /assets/faces/233.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/233.png -------------------------------------------------------------------------------- /assets/faces/234.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/234.png -------------------------------------------------------------------------------- /assets/faces/235.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/235.png -------------------------------------------------------------------------------- /assets/faces/236.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/236.png -------------------------------------------------------------------------------- /assets/faces/237.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/237.png -------------------------------------------------------------------------------- /assets/faces/238.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/238.png -------------------------------------------------------------------------------- /assets/faces/239.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/239.png -------------------------------------------------------------------------------- /assets/faces/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/24.png -------------------------------------------------------------------------------- /assets/faces/240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/240.png -------------------------------------------------------------------------------- /assets/faces/241.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/241.png -------------------------------------------------------------------------------- /assets/faces/242.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/242.png -------------------------------------------------------------------------------- /assets/faces/243.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/243.png -------------------------------------------------------------------------------- /assets/faces/244.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/244.png -------------------------------------------------------------------------------- /assets/faces/245.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/245.png -------------------------------------------------------------------------------- /assets/faces/246.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/246.png -------------------------------------------------------------------------------- /assets/faces/247.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/247.png -------------------------------------------------------------------------------- /assets/faces/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/25.png -------------------------------------------------------------------------------- /assets/faces/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/26.png -------------------------------------------------------------------------------- /assets/faces/260.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/260.png -------------------------------------------------------------------------------- /assets/faces/261.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/261.png -------------------------------------------------------------------------------- /assets/faces/262.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/262.png -------------------------------------------------------------------------------- /assets/faces/263.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/263.png -------------------------------------------------------------------------------- /assets/faces/264.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/264.png -------------------------------------------------------------------------------- /assets/faces/265.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/265.png -------------------------------------------------------------------------------- /assets/faces/266.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/266.png -------------------------------------------------------------------------------- /assets/faces/267.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/267.png -------------------------------------------------------------------------------- /assets/faces/268.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/268.png -------------------------------------------------------------------------------- /assets/faces/269.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/269.png -------------------------------------------------------------------------------- /assets/faces/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/27.png -------------------------------------------------------------------------------- /assets/faces/270.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/270.png -------------------------------------------------------------------------------- /assets/faces/271.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/271.png -------------------------------------------------------------------------------- /assets/faces/272.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/272.png -------------------------------------------------------------------------------- /assets/faces/273.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/273.png -------------------------------------------------------------------------------- /assets/faces/274.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/274.png -------------------------------------------------------------------------------- /assets/faces/276.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/276.png -------------------------------------------------------------------------------- /assets/faces/277.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/277.png -------------------------------------------------------------------------------- /assets/faces/278.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/278.png -------------------------------------------------------------------------------- /assets/faces/279.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/279.png -------------------------------------------------------------------------------- /assets/faces/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/28.png -------------------------------------------------------------------------------- /assets/faces/280.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/280.png -------------------------------------------------------------------------------- /assets/faces/281.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/281.png -------------------------------------------------------------------------------- /assets/faces/282.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/282.png -------------------------------------------------------------------------------- /assets/faces/283.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/283.png -------------------------------------------------------------------------------- /assets/faces/284.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/284.png -------------------------------------------------------------------------------- /assets/faces/285.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/285.png -------------------------------------------------------------------------------- /assets/faces/286.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/286.png -------------------------------------------------------------------------------- /assets/faces/287.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/287.png -------------------------------------------------------------------------------- /assets/faces/288.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/288.png -------------------------------------------------------------------------------- /assets/faces/289.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/289.png -------------------------------------------------------------------------------- /assets/faces/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/29.png -------------------------------------------------------------------------------- /assets/faces/290.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/290.png -------------------------------------------------------------------------------- /assets/faces/291.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/291.png -------------------------------------------------------------------------------- /assets/faces/292.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/292.png -------------------------------------------------------------------------------- /assets/faces/293.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/293.png -------------------------------------------------------------------------------- /assets/faces/294.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/294.png -------------------------------------------------------------------------------- /assets/faces/295.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/295.png -------------------------------------------------------------------------------- /assets/faces/296.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/296.png -------------------------------------------------------------------------------- /assets/faces/297.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/297.png -------------------------------------------------------------------------------- /assets/faces/298.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/298.png -------------------------------------------------------------------------------- /assets/faces/299.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/299.png -------------------------------------------------------------------------------- /assets/faces/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/3.png -------------------------------------------------------------------------------- /assets/faces/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/30.png -------------------------------------------------------------------------------- /assets/faces/300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/300.png -------------------------------------------------------------------------------- /assets/faces/301.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/301.png -------------------------------------------------------------------------------- /assets/faces/302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/302.png -------------------------------------------------------------------------------- /assets/faces/303.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/303.png -------------------------------------------------------------------------------- /assets/faces/304.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/304.png -------------------------------------------------------------------------------- /assets/faces/305.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/305.png -------------------------------------------------------------------------------- /assets/faces/306.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/306.png -------------------------------------------------------------------------------- /assets/faces/307.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/307.png -------------------------------------------------------------------------------- /assets/faces/308.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/308.png -------------------------------------------------------------------------------- /assets/faces/309.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/309.png -------------------------------------------------------------------------------- /assets/faces/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/31.png -------------------------------------------------------------------------------- /assets/faces/310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/310.png -------------------------------------------------------------------------------- /assets/faces/311.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/311.png -------------------------------------------------------------------------------- /assets/faces/312.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/312.png -------------------------------------------------------------------------------- /assets/faces/313.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/313.png -------------------------------------------------------------------------------- /assets/faces/314.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/314.png -------------------------------------------------------------------------------- /assets/faces/315.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/315.png -------------------------------------------------------------------------------- /assets/faces/316.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/316.png -------------------------------------------------------------------------------- /assets/faces/317.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/317.png -------------------------------------------------------------------------------- /assets/faces/318.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/318.png -------------------------------------------------------------------------------- /assets/faces/319.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/319.png -------------------------------------------------------------------------------- /assets/faces/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/32.png -------------------------------------------------------------------------------- /assets/faces/320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/320.png -------------------------------------------------------------------------------- /assets/faces/321.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/321.png -------------------------------------------------------------------------------- /assets/faces/322.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/322.png -------------------------------------------------------------------------------- /assets/faces/323.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/323.png -------------------------------------------------------------------------------- /assets/faces/324.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/324.png -------------------------------------------------------------------------------- /assets/faces/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/33.png -------------------------------------------------------------------------------- /assets/faces/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/34.png -------------------------------------------------------------------------------- /assets/faces/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/35.png -------------------------------------------------------------------------------- /assets/faces/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/36.png -------------------------------------------------------------------------------- /assets/faces/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/37.png -------------------------------------------------------------------------------- /assets/faces/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/38.png -------------------------------------------------------------------------------- /assets/faces/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/39.png -------------------------------------------------------------------------------- /assets/faces/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/4.png -------------------------------------------------------------------------------- /assets/faces/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/40.png -------------------------------------------------------------------------------- /assets/faces/41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/41.png -------------------------------------------------------------------------------- /assets/faces/42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/42.png -------------------------------------------------------------------------------- /assets/faces/43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/43.png -------------------------------------------------------------------------------- /assets/faces/44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/44.png -------------------------------------------------------------------------------- /assets/faces/45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/45.png -------------------------------------------------------------------------------- /assets/faces/46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/46.png -------------------------------------------------------------------------------- /assets/faces/47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/47.png -------------------------------------------------------------------------------- /assets/faces/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/48.png -------------------------------------------------------------------------------- /assets/faces/49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/49.png -------------------------------------------------------------------------------- /assets/faces/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/5.png -------------------------------------------------------------------------------- /assets/faces/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/50.png -------------------------------------------------------------------------------- /assets/faces/51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/51.png -------------------------------------------------------------------------------- /assets/faces/52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/52.png -------------------------------------------------------------------------------- /assets/faces/53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/53.png -------------------------------------------------------------------------------- /assets/faces/54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/54.png -------------------------------------------------------------------------------- /assets/faces/55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/55.png -------------------------------------------------------------------------------- /assets/faces/56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/56.png -------------------------------------------------------------------------------- /assets/faces/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/57.png -------------------------------------------------------------------------------- /assets/faces/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/58.png -------------------------------------------------------------------------------- /assets/faces/59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/59.png -------------------------------------------------------------------------------- /assets/faces/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/6.png -------------------------------------------------------------------------------- /assets/faces/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/60.png -------------------------------------------------------------------------------- /assets/faces/61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/61.png -------------------------------------------------------------------------------- /assets/faces/62.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/62.png -------------------------------------------------------------------------------- /assets/faces/63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/63.png -------------------------------------------------------------------------------- /assets/faces/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/64.png -------------------------------------------------------------------------------- /assets/faces/65.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/65.png -------------------------------------------------------------------------------- /assets/faces/66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/66.png -------------------------------------------------------------------------------- /assets/faces/67.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/67.png -------------------------------------------------------------------------------- /assets/faces/68.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/68.png -------------------------------------------------------------------------------- /assets/faces/69.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/69.png -------------------------------------------------------------------------------- /assets/faces/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/7.png -------------------------------------------------------------------------------- /assets/faces/70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/70.png -------------------------------------------------------------------------------- /assets/faces/71.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/71.png -------------------------------------------------------------------------------- /assets/faces/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/72.png -------------------------------------------------------------------------------- /assets/faces/73.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/73.png -------------------------------------------------------------------------------- /assets/faces/74.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/74.png -------------------------------------------------------------------------------- /assets/faces/75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/75.png -------------------------------------------------------------------------------- /assets/faces/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/76.png -------------------------------------------------------------------------------- /assets/faces/77.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/77.png -------------------------------------------------------------------------------- /assets/faces/78.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/78.png -------------------------------------------------------------------------------- /assets/faces/79.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/79.png -------------------------------------------------------------------------------- /assets/faces/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/8.png -------------------------------------------------------------------------------- /assets/faces/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/80.png -------------------------------------------------------------------------------- /assets/faces/81.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/81.png -------------------------------------------------------------------------------- /assets/faces/82.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/82.png -------------------------------------------------------------------------------- /assets/faces/83.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/83.png -------------------------------------------------------------------------------- /assets/faces/84.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/84.png -------------------------------------------------------------------------------- /assets/faces/85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/85.png -------------------------------------------------------------------------------- /assets/faces/86.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/86.png -------------------------------------------------------------------------------- /assets/faces/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/87.png -------------------------------------------------------------------------------- /assets/faces/88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/88.png -------------------------------------------------------------------------------- /assets/faces/89.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/89.png -------------------------------------------------------------------------------- /assets/faces/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/9.png -------------------------------------------------------------------------------- /assets/faces/90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/90.png -------------------------------------------------------------------------------- /assets/faces/91.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/91.png -------------------------------------------------------------------------------- /assets/faces/92.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/92.png -------------------------------------------------------------------------------- /assets/faces/93.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/93.png -------------------------------------------------------------------------------- /assets/faces/94.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/94.png -------------------------------------------------------------------------------- /assets/faces/95.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/95.png -------------------------------------------------------------------------------- /assets/faces/96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/96.png -------------------------------------------------------------------------------- /assets/faces/97.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/97.png -------------------------------------------------------------------------------- /assets/faces/98.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/98.png -------------------------------------------------------------------------------- /assets/faces/99.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/assets/faces/99.png -------------------------------------------------------------------------------- /assets/preload.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 该文件在页面生成时自动加载 3 | */ 4 | ;(() => { 5 | 6 | /** 7 | * @type {import("./types").Webview} 8 | */ 9 | const vsc = new window.EventTarget; 10 | vsc.on = vsc.addEventListener; 11 | vsc.TimeoutError = class TimeoutError extends Error { }; 12 | 13 | /** 14 | * @type {Map} 15 | */ 16 | const handlers = new Map; 17 | 18 | /** 19 | * @type {Function} 20 | */ 21 | const postMessage = window.acquireVsCodeApi().postMessage; 22 | window.acquireVsCodeApi = () => { 23 | return { postMessage }; 24 | }; 25 | 26 | const env = window.document.querySelector("env"); 27 | vsc.self_uin = Number(env.attributes.self_id?.value); 28 | vsc.nickname = String(env.attributes.nickname?.value); 29 | vsc.c2c = env.attributes.c2c?.value === "1"; 30 | vsc.target_uin = Number(env.attributes.target_id?.value); 31 | vsc.assets_path = env.attributes.path?.value + "/"; 32 | vsc.faces_path = vsc.assets_path + "faces/"; 33 | vsc.t = Number(env.attributes.t?.value); 34 | 35 | /** 36 | * @param {import("oicq").CommonEventData} data 37 | */ 38 | function onHostMessage(data) { 39 | if (!data.echo) { 40 | if (data.post_type === "message" || (data.post_type === "sync" && data.sync_type === "message")) { 41 | vsc.dispatchEvent(new window.CustomEvent("message", { detail: data })); 42 | } else if (data.post_type === "notice") { 43 | vsc.dispatchEvent(new window.CustomEvent("notice", { detail: data })); 44 | } 45 | } else { 46 | handlers.get(data?.echo)?.call(null, data); 47 | handlers.delete(data.echo); 48 | } 49 | } 50 | window.addEventListener("message", function (event) { 51 | onHostMessage(event.data); 52 | }); 53 | 54 | vsc.callApi = (command, params = []) => { 55 | const echo = String(Date.now()) + String(Math.random()); 56 | /** 57 | * @type {import("../src/chat").WebViewPostData} 58 | */ 59 | const obj = { 60 | command, params, echo 61 | }; 62 | return new Promise((resolve, reject) => { 63 | postMessage(obj); 64 | const id = setTimeout(() => { 65 | reject(new vsc.TimeoutError); 66 | handlers.delete(echo); 67 | }, 5500); 68 | handlers.set(echo, (data) => { 69 | clearTimeout(id); 70 | resolve(data); 71 | }); 72 | }); 73 | }; 74 | 75 | /** 76 | * @type {Array} 77 | */ 78 | const available_apis = [ 79 | "sendPrivateMsg", "sendGroupMsg", "deleteMsg", "getChatHistory", 80 | "sendGroupPoke", "setGroupCard", "setGroupAdmin", "setGroupSpecialTitle", 81 | "setGroupKick", "setGroupBan", "setGroupWholeBan", "setGroupAnonymousBan", 82 | "getForwardMsg", "getGroupInfo", "getGroupMemberList", "getGroupMemberInfo", 83 | "getStrangerInfo", "getGroupNotice", "getRoamingStamp", "getMsg" 84 | ]; 85 | 86 | for (let name of available_apis) { 87 | vsc[name] = (...args) => vsc.callApi(name, args); 88 | } 89 | 90 | vsc.sendMsg = (message, auto_escape = false) => { 91 | const method = vsc.c2c ? "sendPrivateMsg" : "sendGroupMsg"; 92 | return vsc.callApi(method, [vsc.target_uin, message, auto_escape]); 93 | }; 94 | 95 | vsc.scrollHome = () => window.scroll(0, 0); 96 | vsc.scrollEnd = () => window.scroll(0, window.document.body.scrollHeight); 97 | vsc.getUserAvaterUrlSmall = (uin) => `https://q1.qlogo.cn/g?b=qq&s=100&nk=${uin}&t=` + vsc.t; 98 | vsc.getUserAvaterUrlLarge = (uin) => `https://q1.qlogo.cn/g?b=qq&s=640&nk=${uin}&t=` + vsc.t; 99 | vsc.getGroupAvaterUrlSmall = (uin) => `https://p.qlogo.cn/gh/${uin}/${uin}/100?t=` + vsc.t; 100 | vsc.getGroupAvaterUrlLarge = (uin) => `https://p.qlogo.cn/gh/${uin}/${uin}/640?t=` + vsc.t; 101 | 102 | vsc.timestamp = (unixstamp) => { 103 | const date = new Date(unixstamp ? unixstamp * 1000 : Date.now()); 104 | return date.getHours() 105 | + ":" 106 | + String(date.getMinutes()).padStart(2, "0") 107 | + ":" 108 | + String(date.getSeconds()).padStart(2, "0"); 109 | }; 110 | vsc.datetime = (unixstamp) => { 111 | const date = new Date(unixstamp ? unixstamp * 1000 : Date.now()); 112 | return date.getFullYear() 113 | + "/" 114 | + String(date.getMonth() + 1).padStart(2, "0") 115 | + "/" 116 | + String(date.getDate()).padStart(2, "0") 117 | + " " 118 | + vsc.timestamp(unixstamp); 119 | }; 120 | 121 | window.webview = vsc; 122 | 123 | })(window); 124 | -------------------------------------------------------------------------------- /assets/types.d.ts: -------------------------------------------------------------------------------- 1 | import * as oicq from "oicq"; 2 | type MessageEventData = oicq.PrivateMessageEventData | oicq.GroupMessageEventData; 3 | type NoticeEventData = oicq.FriendNoticeEventData | oicq.GroupNoticeEventData; 4 | 5 | /** 6 | * webview类型参考 7 | */ 8 | export interface Webview extends EventTarget { 9 | readonly self_uin: number; //自己账号 10 | readonly nickname: string; //自己昵称 11 | readonly c2c: boolean; //私聊为true,群聊为false 12 | readonly target_uin: number; //私聊时为对方账号,群聊时为群号 13 | readonly assets_path: string; //assets文件夹路径("/"结尾) 14 | readonly faces_path: string; //表情文件夹路径("/"结尾) 15 | readonly t: number; //vsc启动时间戳,用于解决头像缓存问题 16 | readonly TimeoutError: typeof Error; 17 | 18 | // 监听新消息事件 19 | on(type: "message", listener: (data: CustomEvent) => void): void; 20 | // 监听新系统通知事件 21 | on(type: "notice", listener: (data: CustomEvent) => void): void; 22 | 23 | callApi(command: keyof oicq.Client, params?: any[]): Promise>; 24 | 25 | sendMsg(message: string | oicq.MessageElem | Iterable, auto_escape?: boolean): Promise>; 26 | sendPrivateMsg: oicq.Client["sendPrivateMsg"]; 27 | sendGroupMsg: oicq.Client["sendGroupMsg"]; 28 | deleteMsg: oicq.Client["deleteMsg"]; 29 | getChatHistory: oicq.Client["getChatHistory"]; 30 | sendGroupPoke: oicq.Client["sendGroupPoke"]; 31 | setGroupCard: oicq.Client["setGroupCard"]; 32 | setGroupAdmin: oicq.Client["setGroupAdmin"]; 33 | setGroupSpecialTitle: oicq.Client["setGroupSpecialTitle"]; 34 | setGroupKick: oicq.Client["setGroupKick"]; 35 | setGroupBan: oicq.Client["setGroupBan"]; 36 | setGroupWholeBan: oicq.Client["setGroupWholeBan"]; 37 | setGroupAnonymousBan: oicq.Client["setGroupAnonymousBan"]; 38 | 39 | getStrangerInfo: oicq.Client["getStrangerInfo"]; 40 | getGroupInfo: oicq.Client["getGroupInfo"]; 41 | getGroupMemberList(uin: number): Promise>; 42 | getGroupMemberInfo: oicq.Client["getGroupMemberInfo"]; 43 | getForwardMsg: oicq.Client["getForwardMsg"]; 44 | getGroupNotice: oicq.Client["getGroupNotice"]; 45 | getRoamingStamp: oicq.Client["getRoamingStamp"]; 46 | getMsg: oicq.Client["getMsg"]; 47 | 48 | scrollHome(): void; 49 | scrollEnd(): void; 50 | timestamp(unixtime?: number): string; 51 | datetime(unixtime?: number): string; 52 | getUserAvaterUrlSmall(uin: number): string; 53 | getUserAvaterUrlLarge(uin: number): string; 54 | getGroupAvaterUrlSmall(uin: number): string; 55 | getGroupAvaterUrlLarge(uin: number): string; 56 | } 57 | -------------------------------------------------------------------------------- /ico.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/ico.ico -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-qq", 3 | "displayName": "QQ", 4 | "description": "lite qq for chat in working", 5 | "version": "1.4.2", 6 | "engines": { 7 | "vscode": "^1.53.0" 8 | }, 9 | "categories": [ 10 | "Other" 11 | ], 12 | "activationEvents": [ 13 | "*" 14 | ], 15 | "main": "./out/extension.js", 16 | "contributes": { 17 | "commands": [ 18 | { 19 | "command": "oicq.contact.pin", 20 | "title": "固定" 21 | }, 22 | { 23 | "command": "oicq.contact.unpin", 24 | "title": "取消固定" 25 | }, 26 | { 27 | "command": "oicq.contact.profile", 28 | "title": "查看资料" 29 | }, 30 | { 31 | "command": "oicq.tooltip.copy", 32 | "title": "拷贝标签" 33 | }, 34 | { 35 | "command": "oicq.friend.delete", 36 | "title": "删除好友" 37 | }, 38 | { 39 | "command": "oicq.group.delete", 40 | "title": "退群" 41 | }, 42 | { 43 | "command": "oicq.group.invite", 44 | "title": "邀请好友入群" 45 | }, 46 | { 47 | "command": "oicq.statusBar.click", 48 | "title": "Login", 49 | "category": "QQ Explorer" 50 | }, 51 | { 52 | "command": "oicq.friend.search", 53 | "title": "搜索好友", 54 | "category": "QQ Explorer" 55 | }, 56 | { 57 | "command": "oicq.group.search", 58 | "title": "搜索群", 59 | "category": "QQ Explorer" 60 | }, 61 | { 62 | "command": "oicq.pinned.refresh", 63 | "icon": "$(refresh)", 64 | "title": "刷新" 65 | }, 66 | { 67 | "command": "oicq.friends.refresh", 68 | "icon": "$(refresh)", 69 | "title": "刷新" 70 | }, 71 | { 72 | "command": "oicq.groups.refresh", 73 | "icon": "$(refresh)", 74 | "title": "刷新" 75 | } 76 | ], 77 | "viewsContainers": { 78 | "activitybar": [ 79 | { 80 | "id": "qq-explorer", 81 | "title": "QQ Explorer", 82 | "icon": "ico.ico" 83 | } 84 | ] 85 | }, 86 | "views": { 87 | "qq-explorer": [ 88 | { 89 | "id": "chat-pinned", 90 | "name": "固定" 91 | }, 92 | { 93 | "id": "chat-friends", 94 | "name": "好友列表" 95 | }, 96 | { 97 | "id": "chat-groups", 98 | "name": "群列表" 99 | } 100 | ] 101 | }, 102 | "menus": { 103 | "view/item/context": [ 104 | { 105 | "command": "oicq.contact.pin", 106 | "when": "view == chat-friends || view == chat-groups" 107 | }, 108 | { 109 | "command": "oicq.contact.unpin", 110 | "when": "view == chat-pinned" 111 | }, 112 | { 113 | "command": "oicq.contact.profile", 114 | "when": "view == chat-friends || view == chat-groups || view == chat-pinned" 115 | }, 116 | { 117 | "command": "oicq.tooltip.copy", 118 | "when": "view == chat-friends || view == chat-groups || view == chat-pinned" 119 | }, 120 | { 121 | "command": "oicq.friend.delete", 122 | "when": "view == chat-friends" 123 | }, 124 | { 125 | "command": "oicq.group.invite", 126 | "when": "view == chat-friends || view == chat-groups || view == chat-pinned" 127 | }, 128 | { 129 | "command": "oicq.group.delete", 130 | "when": "view == chat-groups" 131 | } 132 | ], 133 | "view/title": [ 134 | { 135 | "command": "oicq.pinned.refresh", 136 | "group": "navigation", 137 | "when": "view == chat-pinned" 138 | }, 139 | { 140 | "command": "oicq.friends.refresh", 141 | "group": "navigation", 142 | "when": "view == chat-friends" 143 | }, 144 | { 145 | "command": "oicq.groups.refresh", 146 | "group": "navigation", 147 | "when": "view == chat-groups" 148 | } 149 | ] 150 | } 151 | }, 152 | "scripts": { 153 | "vscode:prepublish": "npm run compile", 154 | "compile": "tsc -p ./", 155 | "watch": "tsc -watch -p ./", 156 | "pretest": "npm run compile && npm run lint", 157 | "lint": "eslint src --ext ts", 158 | "test": "node ./out/test/runTest.js" 159 | }, 160 | "devDependencies": { 161 | "@types/glob": "^7.1.4", 162 | "@types/mocha": "^8.2.3", 163 | "@types/node": "^12.20.16", 164 | "@types/vscode": "^1.53.0", 165 | "@types/ws": "^7.4.7", 166 | "@typescript-eslint/eslint-plugin": "^4.28.4", 167 | "@typescript-eslint/parser": "^4.28.4", 168 | "eslint": "^7.31.0", 169 | "glob": "^7.1.7", 170 | "mocha": "^8.4.0", 171 | "typescript": "^4.3.5", 172 | "vscode-test": "^1.6.1" 173 | }, 174 | "dependencies": { 175 | "get-port": "^5.1.1", 176 | "oicq": "^1.21.3", 177 | "ws": "^7.5.6" 178 | }, 179 | "publisher": "takayama", 180 | "license": "MPL-2.0", 181 | "keywords": [ 182 | "qq", 183 | "oicq" 184 | ], 185 | "repository": { 186 | "type": "git", 187 | "url": "https://github.com/takayama-lily/vscode-qq" 188 | }, 189 | "icon": "ico.ico" 190 | } 191 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takayama-lily/vscode-qq/7d7b69c83aa96703c6a9b319cb0382b2cd65d2af/preview.gif -------------------------------------------------------------------------------- /src/cdp.ts: -------------------------------------------------------------------------------- 1 | import * as child_process from "child_process"; 2 | import * as EventEmitter from "events"; 3 | import * as http from "http"; 4 | import * as os from "os"; 5 | import * as fs from "fs"; 6 | import * as path from "path"; 7 | import * as WebSocket from "ws"; 8 | import * as getAnUnusedPort from "get-port"; 9 | 10 | const tmpdir = path.join(os.tmpdir(), "vscode-qq"); 11 | const params = [ 12 | "--disable-extensions", 13 | "--enable-automation", 14 | "--no-sandbox", 15 | "--headeless", 16 | "--disable-gpu", 17 | ]; 18 | 19 | export const NO_CHROME_ERROR = Symbol("no chrome"); 20 | export const TIMEOUT_ERROR = Symbol("failed too many times"); 21 | export const UNFINISHED_ERROR = Symbol("chrome closed but no ticket"); 22 | 23 | export class Cdp extends EventEmitter { 24 | private port = 0; 25 | private url = ""; 26 | private webSocketDebuggerUrl = ""; 27 | private ticket = ""; 28 | 29 | private async openChrome(url: string) { 30 | this.port = await getAnUnusedPort(); 31 | if (!fs.existsSync(tmpdir)) { 32 | fs.mkdirSync(tmpdir); 33 | } 34 | let cmd = ""; 35 | if (os.platform().includes("win")) { 36 | cmd = "cmd /c start chrome.exe"; 37 | } else { 38 | cmd = "chrome"; 39 | } 40 | cmd += ` "${url}" `; 41 | cmd += params.join(" ") 42 | + " --user-data-dir=" + tmpdir 43 | + " --remote-debugging-port=" + this.port; 44 | this.url = url; 45 | child_process.execSync(cmd); 46 | } 47 | 48 | private getWebSocketDebuggerUrl() { 49 | http.get("http://localhost:" + this.port + "/json/list", (res) => { 50 | res.setEncoding("utf-8"); 51 | let data = ""; 52 | res.on("data", (chunk) => data += chunk); 53 | res.on("end", () => { 54 | try { 55 | const obj = JSON.parse(data); 56 | for (let o of obj) { 57 | if (o.url === this.url) { 58 | this.webSocketDebuggerUrl = o.webSocketDebuggerUrl; 59 | } 60 | } 61 | } catch { } 62 | }); 63 | }).on("error", () => { }); 64 | } 65 | 66 | private _getTicket() { 67 | const ws = new WebSocket(this.webSocketDebuggerUrl); 68 | ws.on("open", () => { 69 | ws.send(JSON.stringify({ 70 | id: 1, 71 | method: "Network.enable" 72 | })); 73 | }); 74 | ws.on("error", () => {}); 75 | ws.on("close", () => { 76 | if (!this.ticket) { 77 | this.emit("error", UNFINISHED_ERROR); 78 | } else { 79 | this.emit("ticket", this.ticket); 80 | } 81 | }); 82 | ws.on("message", (data) => { 83 | try { 84 | const obj = JSON.parse(String(data)); 85 | if (obj.method === "Network.responseReceived" && obj.params.type === "XHR" && obj.params.response.url === "https://t.captcha.qq.com/cap_union_new_verify") { 86 | ws.send(JSON.stringify({ 87 | id: 2, 88 | method: "Network.getResponseBody", 89 | params: { 90 | requestId: obj.params.requestId 91 | }, 92 | })); 93 | } else if (obj.id === 2) { 94 | const body = JSON.parse(obj.result.body); 95 | this.ticket = body.ticket; 96 | if (this.ticket) { 97 | ws.send(JSON.stringify({ 98 | id: 3, 99 | method: "Browser.close" 100 | })); 101 | ws.close(); 102 | } 103 | } 104 | } catch { } 105 | }); 106 | } 107 | 108 | public async getTicket(url: string) { 109 | try { 110 | await this.openChrome(url); 111 | } catch { 112 | this.emit("error", NO_CHROME_ERROR); 113 | return; 114 | } 115 | this.getWebSocketDebuggerUrl(); 116 | let times = 1; 117 | const id = setInterval(() => { 118 | ++times; 119 | if (this.webSocketDebuggerUrl) { 120 | clearInterval(id); 121 | this._getTicket(); 122 | } else { 123 | if (times >= 10) { 124 | clearInterval(id); 125 | this.emit("error", TIMEOUT_ERROR); 126 | } else { 127 | this.getWebSocketDebuggerUrl(); 128 | } 129 | } 130 | }, 1000); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/chat.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as oicq from 'oicq'; 3 | import { refreshContacts } from "./explorer"; 4 | import { getConfig } from "./config"; 5 | import { client, ctx, genContactId, parseContactId } from "./global"; 6 | 7 | export interface WebViewPostData { 8 | command: keyof oicq.Client, 9 | params: any[], 10 | echo: string, 11 | } 12 | 13 | vscode.commands.registerCommand("oicq.c2c.open", openChatView); 14 | vscode.commands.registerCommand("oicq.group.open", openChatView); 15 | 16 | const webviewMap: Map = new Map; 17 | 18 | const availableThemes = [ 19 | "default", 20 | "console" 21 | ]; 22 | 23 | const T = Date.now(); 24 | 25 | function getHtml(id: string, webview: vscode.Webview) { 26 | let preload = webview.asWebviewUri(vscode.Uri.joinPath(ctx.extensionUri, "assets", "preload.js")).toString(); 27 | let css: string, js: string; 28 | const config = getConfig(); 29 | if (config.theme_css && config.theme_js) { 30 | if (config.theme_css.startsWith("http")) { 31 | css = config.theme_css; 32 | } else { 33 | css = webview.asWebviewUri(vscode.Uri.file(config.theme_css)).toString(); 34 | } 35 | if (config.theme_js.startsWith("http")) { 36 | js = config.theme_js; 37 | } else { 38 | js = webview.asWebviewUri(vscode.Uri.file(config.theme_js)).toString(); 39 | } 40 | } else { 41 | let theme = "default"; 42 | if (availableThemes.includes(String(config.theme))) { 43 | theme = String(config.theme); 44 | } 45 | css = webview.asWebviewUri(vscode.Uri.joinPath(ctx.extensionUri, "assets", theme + "-theme", "style.css")).toString(); 46 | js = webview.asWebviewUri(vscode.Uri.joinPath(ctx.extensionUri, "assets", theme + "-theme", "app.js")).toString(); 47 | } 48 | const { self, type, uin } = parseContactId(id); 49 | const path = webview.asWebviewUri(vscode.Uri.joinPath(ctx.extensionUri, "assets")).toString(); 50 | return ` 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | `; 64 | } 65 | 66 | const uri_root = vscode.Uri.file("/"); 67 | const uri_c = vscode.Uri.file("c:/"); 68 | const uri_d = vscode.Uri.file("d:/"); 69 | const uri_e = vscode.Uri.file("e:/"); 70 | const uri_f = vscode.Uri.file("f:/"); 71 | const uri_g = vscode.Uri.file("g:/"); 72 | 73 | function openChatView(id: string) { 74 | 75 | const { type, uin } = parseContactId(id); 76 | let label: string; 77 | if (type === "u") { 78 | label = String(client.fl.get(uin)?.nickname); 79 | } else { 80 | label = String(client.gl.get(uin)?.group_name); 81 | } 82 | 83 | if (webviewMap.has(id)) { 84 | return webviewMap.get(id)?.reveal(); 85 | } 86 | const webview = vscode.window.createWebviewPanel("chat", label, -1, { 87 | enableScripts: true, 88 | enableCommandUris: true, 89 | retainContextWhenHidden: true, 90 | localResourceRoots: [ 91 | ctx.extensionUri, 92 | ctx.globalStorageUri, 93 | uri_root, uri_c, uri_d, uri_e, uri_f, uri_g, 94 | ] 95 | }); 96 | webviewMap.set(id, webview); 97 | webview.webview.html = getHtml(id, webview.webview); 98 | webview.reveal(); 99 | webview.onDidDispose(() => { 100 | webviewMap.delete(id); 101 | }); 102 | webview.onDidChangeViewState((event) => { 103 | if (event.webviewPanel.visible) { 104 | refreshContacts(id, false); 105 | } 106 | }); 107 | webview.webview.onDidReceiveMessage(async (data: WebViewPostData) => { 108 | try { 109 | if (data.command === "getChatHistory" && data.params?.[0] === "") { 110 | let buf: Buffer; 111 | if (type === "g") { 112 | buf = Buffer.alloc(21); 113 | } else { 114 | buf = Buffer.alloc(17); 115 | } 116 | buf.writeUInt32BE(uin, 0); 117 | data.params[0] = buf.toString("base64"); 118 | } 119 | const fn = client[data.command]; 120 | if (typeof fn === "function") { 121 | //@ts-ignore 122 | let ret: any = fn.apply(client, Array.isArray(data.params) ? data.params : []); 123 | if (ret instanceof Promise) { 124 | ret = await ret; 125 | } 126 | if (ret.data instanceof Map) { 127 | ret.data = [...ret.data.values()]; 128 | } 129 | ret.echo = data.echo; 130 | webview.webview.postMessage(ret); 131 | } 132 | } catch { } 133 | }); 134 | } 135 | 136 | function postC2CEvent(data: oicq.FriendNoticeEventData | oicq.PrivateMessageEventData | oicq.SyncMessageEventData) { 137 | const id = genContactId("u", data.user_id); 138 | webviewMap.get(id)?.webview.postMessage(data); 139 | } 140 | 141 | function postGroupEvent(data: oicq.GroupNoticeEventData | oicq.GroupMessageEventData) { 142 | const id = genContactId("g", data.group_id); 143 | webviewMap.get(id)?.webview.postMessage(data); 144 | } 145 | 146 | export function bind() { 147 | client.on("message.group", function (data) { 148 | const id = genContactId("g", data.group_id); 149 | if (webviewMap.get(id)?.visible) { 150 | return; 151 | } 152 | refreshContacts(id, true); 153 | }); 154 | 155 | client.on("message.private", function (data) { 156 | const id = genContactId("u", data.user_id); 157 | if (webviewMap.get(id)?.visible) { 158 | return; 159 | } 160 | refreshContacts(id, true); 161 | }); 162 | 163 | client.on("message.group", postGroupEvent); 164 | client.on("message.private", postC2CEvent); 165 | 166 | client.on("notice.group", postGroupEvent); 167 | client.on("notice.friend.recall", postC2CEvent); 168 | client.on("notice.friend.poke", postC2CEvent); 169 | client.on("sync.message", postC2CEvent); 170 | } 171 | -------------------------------------------------------------------------------- /src/client.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import * as crypto from 'crypto'; 4 | import * as vscode from 'vscode'; 5 | import * as oicq from 'oicq'; 6 | import { client, setClient } from "./global"; 7 | import { openConfigFile, deleteToken, genClientConfig, getConfig, setConfig } from "./config"; 8 | import { initLists } from "./explorer"; 9 | import { Cdp } from "./cdp"; 10 | 11 | let logining = false; 12 | let selectedStatus: number = 11; 13 | const statusMap: { [k: number]: string } = { 14 | 11: "我在线上", 15 | 60: "Q我吧", 16 | 31: "离开", 17 | 50: "忙碌", 18 | 70: "请勿打扰", 19 | 41: "隐身", 20 | 0: "离线", 21 | 97: "@个人资料", 22 | 98: "@切换账号", 23 | 99: "@设置", 24 | 100: "@feedback" 25 | }; 26 | 27 | /** 28 | * create the instance 29 | */ 30 | function createClient(uin: number) { 31 | const c = oicq.createClient(uin, genClientConfig()); 32 | 33 | try { 34 | const stat = fs.statSync(path.join(c.dir, "online.lock"), { bigint: true }); 35 | const diff = Date.now() - Number(stat.mtimeMs); 36 | if (diff >= 0 && diff < 10000) { 37 | vscode.window.showErrorMessage("你已经在另一个Code中登录了此账号。"); 38 | return; 39 | } 40 | } catch { } 41 | 42 | setClient(c); 43 | 44 | client.on("system.login.error", function (data) { 45 | logining = false; 46 | if (data.message.includes("密码错误")) { 47 | setConfig({ 48 | account: 0, 49 | password: "" 50 | }); 51 | data.message += "(请选择:@切换账号)"; 52 | } 53 | vscode.window.showErrorMessage(data.message); 54 | 55 | }); 56 | client.on("system.login.qrcode", function (data) { 57 | const webview = vscode.window.createWebviewPanel("device", "手机QQ扫码登录 (完成后请关闭)", -1, { 58 | enableScripts: true, 59 | enableCommandUris: true 60 | }); 61 | webview.webview.html = `若提示二维码已过期重新获取一次
`; 62 | webview.reveal(); 63 | webview.onDidDispose(() => { 64 | client.login(); 65 | }); 66 | }); 67 | client.on("system.login.slider", function (data) { 68 | const cdp = new Cdp; 69 | cdp.on("ticket", (ticket: string) => { 70 | client.sliderLogin(ticket); 71 | }); 72 | cdp.on("error", (err: Symbol) => { 73 | vscode.window.showInformationMessage(`打开chrome失败,请 [点我](${data.url}) 完成滑动验证码并获取ticket (按F12查看网络请求以获取) [教程](https://github.com/takayama-lily/oicq/wiki/01.%E6%BB%91%E5%8A%A8%E9%AA%8C%E8%AF%81%E7%A0%81%E5%92%8C%E8%AE%BE%E5%A4%87%E9%94%81)`); 74 | inputTicket(); 75 | }); 76 | cdp.getTicket(data.url); 77 | }); 78 | client.on("system.login.device", function (data) { 79 | const webview = vscode.window.createWebviewPanel("device", "[QQ]需要验证设备安全性 (完成后请关闭)", -1, { 80 | enableScripts: true, 81 | enableCommandUris: true 82 | }); 83 | webview.webview.html = ``; 84 | webview.reveal(); 85 | webview.onDidDispose(() => { 86 | client.login(); 87 | }); 88 | vscode.window.showInformationMessage(`[设备锁验证地址](${data.url}) 若vscode中无法正常显示可在浏览器中打开`); 89 | }); 90 | client.on("system.offline", (data) => { 91 | logining = false; 92 | if (data.message.includes("服务器繁忙")) { 93 | data.message = "服务器繁忙,请过几秒后再次尝试。"; 94 | } 95 | vscode.window.showErrorMessage(data.message); 96 | }); 97 | client.on("system.online", function () { 98 | logining = false; 99 | if (selectedStatus !== 11) { 100 | this.setOnlineStatus(selectedStatus); 101 | } 102 | setConfig({ 103 | account: this.uin, 104 | password: this.password_md5 ? this.password_md5.toString("hex") : "qrcode" 105 | }); 106 | vscode.window.showInformationMessage(`${client.nickname}(${client.uin}) 已上线`); 107 | initLists(); 108 | }); 109 | 110 | inputPassword(); 111 | } 112 | 113 | /** 114 | * input account 115 | */ 116 | function inputAccount() { 117 | const uin = Number(getConfig().account); 118 | if (uin > 10000 && uin < 0xffffffff) { 119 | return createClient(uin); 120 | } 121 | vscode.window.showInputBox({ 122 | placeHolder: "输入QQ账号...", 123 | }).then((uin) => { 124 | if (!uin) { 125 | return; 126 | } 127 | try { 128 | createClient(Number(uin)); 129 | } catch { 130 | inputAccount(); 131 | } 132 | }); 133 | } 134 | 135 | /** 136 | * input password of account 137 | */ 138 | function inputPassword() { 139 | const password = String(getConfig().password); 140 | if (password === "qrcode") { 141 | return client.login(); 142 | } else if (password) { 143 | return client.login(password); 144 | } 145 | vscode.window.showInputBox({ 146 | placeHolder: "输入密码... (扫码登录请留空)", 147 | prompt: `输入账号 ${client.uin} 的密码`, 148 | password: true 149 | }).then((pass) => { 150 | if (!pass) { 151 | return client.login(); 152 | } 153 | const password = crypto.createHash("md5").update(pass).digest(); 154 | logining = true; 155 | client.login(password); 156 | }); 157 | } 158 | 159 | /** 160 | * input ticket from slider catpcha 161 | */ 162 | function inputTicket() { 163 | vscode.window.showInputBox({ placeHolder: "输入验证码ticket" }) 164 | .then((ticket) => { 165 | if (!ticket) { 166 | inputTicket(); 167 | } else { 168 | client.sliderLogin(ticket); 169 | } 170 | }); 171 | } 172 | 173 | function showProfile() { 174 | const arr = [ 175 | "账号:" + client.uin + " (点击复制)", 176 | "昵称:" + client.nickname + " (点击设置)", 177 | "性别:" + client.sex + " (点击设置)", 178 | "年龄:" + client.age + " (点击设置)", 179 | "个性签名 (点击设置)" 180 | ]; 181 | vscode.window.showQuickPick(arr).then((value) => { 182 | switch (value) { 183 | case arr[0]: 184 | vscode.env.clipboard.writeText(`${client.nickname} (${client.uin})`); 185 | break; 186 | case arr[1]: 187 | vscode.window.showInputBox({ placeHolder: "输入新的昵称...", prompt: "当前昵称为:" + client.nickname}) 188 | .then((value) => { 189 | if (value) { 190 | client.setNickname(value); 191 | } 192 | }); 193 | break; 194 | case arr[2]: 195 | vscode.window.showInputBox({ placeHolder: "输入性别数字...", prompt: "0: unknown; 1: male; 2: female"}) 196 | .then((value) => { 197 | if (value) { 198 | //@ts-ignore 199 | client.setGender(Number(value)); 200 | } 201 | }); 202 | break; 203 | case arr[3]: 204 | vscode.window.showInputBox({ placeHolder: "输入生日...", prompt: "格式为:20020202"}) 205 | .then((value) => { 206 | if (value) { 207 | client.setBirthday(value); 208 | } 209 | }); 210 | break; 211 | case arr[4]: 212 | vscode.window.showInputBox({ placeHolder: "输入个性签名..."}) 213 | .then((value) => { 214 | if (value) { 215 | client.setSignature(value); 216 | } 217 | }); 218 | break; 219 | } 220 | }); 221 | } 222 | 223 | export function invoke() { 224 | const tmp = { ...statusMap }; 225 | if (!client || !client.isOnline()) { 226 | tmp[0] += " (当前)"; 227 | } else { 228 | tmp[client.online_status] += " (当前)"; 229 | } 230 | const arr = Object.values(tmp); 231 | vscode.window.showQuickPick(arr) 232 | .then((value) => { 233 | if (value === "@设置") { 234 | return openConfigFile(); 235 | } 236 | if (logining) { 237 | vscode.window.showInformationMessage("正在登录中,请稍后..."); 238 | return; 239 | } 240 | if (value === "@切换账号") { 241 | client?.logout(); 242 | deleteToken(); 243 | setConfig({ 244 | account: 0, 245 | password: "" 246 | }); 247 | return inputAccount(); 248 | } 249 | if (value === "@个人资料") { 250 | if (client) { 251 | showProfile(); 252 | } 253 | return; 254 | } 255 | if (value === "@feedback") { 256 | vscode.env.openExternal(vscode.Uri.parse("https://github.com/takayama-lily/vscode-qq/issues")); 257 | return; 258 | } 259 | if (value?.includes("离线")) { 260 | client?.logout(); 261 | } else if (value) { 262 | const i = arr.indexOf(value); 263 | selectedStatus = Number(Object.keys(statusMap)[i]); 264 | if (client) { 265 | if (!client.isOnline()) { 266 | client.login(); 267 | } else { 268 | client.setOnlineStatus(selectedStatus); 269 | } 270 | } else { 271 | inputAccount(); 272 | } 273 | } 274 | }); 275 | } 276 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | import * as oicq from 'oicq'; 4 | import * as vscode from 'vscode'; 5 | import { ctx, client, NOOP } from "./global"; 6 | 7 | interface Config extends oicq.ConfBot { 8 | account?: number, 9 | password?: string, 10 | show_me_add_group_request?: boolean, 11 | theme?: string, 12 | theme_css?: string, 13 | theme_js?: string, 14 | } 15 | 16 | const optimized: Config = { 17 | account: 0, 18 | password: "", 19 | platform: 5, 20 | show_me_add_group_request: false, 21 | theme: "default", 22 | theme_css: "", 23 | theme_js: "", 24 | }; 25 | 26 | let config: Config | undefined; 27 | 28 | function getConfigFilePath() { 29 | return path.join(ctx.globalStoragePath, "config.json"); 30 | } 31 | 32 | export function getConfig(): Config { 33 | if (!config) { 34 | try { 35 | config = JSON.parse(fs.readFileSync(getConfigFilePath(), { encoding: "utf-8" })); 36 | } catch { 37 | fs.writeFile(getConfigFilePath(), JSON.stringify(optimized, null, 2), NOOP); 38 | config = { ...optimized }; 39 | } 40 | } 41 | //@ts-ignore 42 | return config; 43 | } 44 | 45 | export function setConfig(obj: Config) { 46 | Object.assign(getConfig(), obj); 47 | fs.writeFile(getConfigFilePath(), JSON.stringify(config, null, 2), NOOP); 48 | } 49 | 50 | export function genClientConfig() { 51 | const clientConfig: oicq.ConfBot = { 52 | log_level: "off", 53 | kickoff: false, 54 | ignore_self: false, 55 | brief: true, 56 | reconn_interval: 0, 57 | data_dir: ctx.globalStoragePath, 58 | }; 59 | return Object.assign(clientConfig, getConfig()); 60 | } 61 | 62 | let watcherCreatedFlag = false; 63 | export function openConfigFile() { 64 | getConfig(); 65 | const uri = vscode.Uri.file(getConfigFilePath()); 66 | vscode.window.showTextDocument(uri); 67 | if (!watcherCreatedFlag) { 68 | watcherCreatedFlag = true; 69 | vscode.workspace.createFileSystemWatcher(getConfigFilePath(), true, false, true).onDidChange(async () => { 70 | try { 71 | config = JSON.parse(await fs.promises.readFile(getConfigFilePath(), { encoding: "utf-8" })); 72 | } catch { 73 | vscode.window.showErrorMessage("配置文件中有错误,请检查。"); 74 | } 75 | }); 76 | } 77 | } 78 | 79 | export function deleteToken() { 80 | if (client) { 81 | fs.unlink(path.join(client.dir, "token"), NOOP); 82 | fs.unlink(path.join(client.dir, "t106"), NOOP); 83 | } 84 | } 85 | 86 | export function writePinned(pinned: string[]) { 87 | fs.writeFile(path.join(client.dir, "pinned"), pinned.join("\n"), NOOP); 88 | } 89 | 90 | export async function readPinned() { 91 | try { 92 | const pinned = await fs.promises.readFile(path.join(client.dir, "pinned"), { encoding: "utf-8" }); 93 | return String(pinned).split("\n"); 94 | } catch { 95 | return []; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/explorer.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { readPinned, writePinned } from "./config"; 3 | import { client, genContactId, parseContactId } from "./global"; 4 | import * as chat from "./chat"; 5 | 6 | // 声明 7 | 8 | let friendListTreeDataProvider: FriendListTreeDataProvider; 9 | let groupListTreeDataProvider: GroupListTreeDataProvider; 10 | let pinnedTreeDataProvider: PinnedTreeDataProvider; 11 | let itemMap: Map = new Map; 12 | let firendTreeView: vscode.TreeView; 13 | let groupTreeView: vscode.TreeView; 14 | 15 | class ContactTreeItem extends vscode.TreeItem { 16 | new = 0; 17 | pinned = false; 18 | id: string; 19 | constructor(id: string) { 20 | super(id); 21 | this.id = id; 22 | }; 23 | updateLabel(emoji: string, name?: string, remark?: string) { 24 | this.label = emoji + (remark || name) + (this.new > 0 ? ` (+${this.new})` : ""); 25 | this.tooltip = emoji + name + ` (${parseContactId(this.id).uin})`; 26 | } 27 | } 28 | 29 | abstract class ContactListTreeDataProvider implements vscode.TreeDataProvider { 30 | _onDidChangeTreeData = new vscode.EventEmitter(); 31 | onDidChangeTreeData = this._onDidChangeTreeData.event; 32 | abstract getChildren(): string[] | Promise; 33 | getParent(id: string) { 34 | return null; 35 | } 36 | getTreeItem(id: string) { 37 | const { type, uin } = parseContactId(id); 38 | let item = itemMap.get(id); 39 | if (!item) { 40 | item = new ContactTreeItem(id); 41 | if (type === "u") { 42 | item.command = { 43 | title: "打开私聊", command: "oicq.c2c.open", arguments: [id] 44 | }; 45 | } else { 46 | item.command = { 47 | title: "打开群聊", command: "oicq.group.open", arguments: [id] 48 | }; 49 | } 50 | itemMap.set(id, item); 51 | } 52 | if (type === "u") { 53 | const friend = client.fl.get(uin); 54 | const emoji = friend?.sex === "female" ? "🙎‍♀️" : "🙎‍♂️"; 55 | item.updateLabel(emoji, friend?.nickname, friend?.remark); 56 | } else { 57 | const group = client.gl.get(uin); 58 | item.updateLabel("👨‍👦‍👦", group?.group_name); 59 | } 60 | return item; 61 | } 62 | refresh(id?: string) { 63 | this._onDidChangeTreeData.fire(id); 64 | } 65 | } 66 | 67 | class FriendListTreeDataProvider extends ContactListTreeDataProvider { 68 | getChildren() { 69 | const children: string[] = []; 70 | for (const uin of client?.fl.keys()) { 71 | const id = genContactId("u", uin); 72 | if (itemMap.get(id)?.new) { 73 | children.unshift(id); 74 | } else { 75 | children.push(id); 76 | } 77 | } 78 | return children; 79 | } 80 | } 81 | 82 | class GroupListTreeDataProvider extends ContactListTreeDataProvider { 83 | getChildren() { 84 | const children: string[] = []; 85 | for (const uin of client?.gl.keys()) { 86 | const id = genContactId("g", uin); 87 | if (itemMap.get(id)?.new) { 88 | children.unshift(id); 89 | } else { 90 | children.push(id); 91 | } 92 | } 93 | return children; 94 | } 95 | } 96 | 97 | class PinnedTreeDataProvider extends ContactListTreeDataProvider { 98 | inited = false; 99 | async getChildren() { 100 | const children: string[] = []; 101 | if (this.inited) { 102 | for (const item of itemMap.values()) { 103 | if (item.pinned) { 104 | children.push(item.id); 105 | } 106 | } 107 | } else { 108 | const pinned = await readPinned(); 109 | for (const item of itemMap.values()) { 110 | if (pinned.includes(item.id)) { 111 | item.pinned = true; 112 | children.push(item.id); 113 | } 114 | } 115 | this.inited = true; 116 | } 117 | return children; 118 | } 119 | } 120 | 121 | // 注册指令 122 | 123 | vscode.commands.registerCommand("oicq.contact.pin", (id: string) => { 124 | const item = itemMap.get(id); 125 | if (item) { 126 | item.pinned = true; 127 | } 128 | pinnedTreeDataProvider.refresh(); 129 | pinnedTreeDataProvider.getChildren().then(writePinned); 130 | }); 131 | vscode.commands.registerCommand("oicq.contact.unpin", (id: string) => { 132 | const item = itemMap.get(id); 133 | if (item) { 134 | item.pinned = false; 135 | } 136 | pinnedTreeDataProvider.refresh(); 137 | pinnedTreeDataProvider.getChildren().then(writePinned); 138 | }); 139 | 140 | vscode.commands.registerCommand("oicq.tooltip.copy", (id: string) => { 141 | const tooltip = itemMap.get(id)?.tooltip; 142 | if (tooltip) { 143 | vscode.env.clipboard.writeText(String(tooltip)); 144 | } 145 | }); 146 | 147 | vscode.commands.registerCommand("oicq.contact.profile", async (id: string) => { 148 | const { uin, type } = parseContactId(id); 149 | const arr: string[] = []; 150 | if (type === "u") { 151 | const data = (await client.getStrangerInfo(uin, true)).data; 152 | if (data) { 153 | arr.push("账号:" + data.user_id); 154 | arr.push("昵称:" + data.nickname); 155 | arr.push("性别:" + data.sex); 156 | arr.push("年龄:" + data.age); 157 | arr.push("地区:" + data.area); 158 | } 159 | } else { 160 | const data = (await client.getGroupInfo(uin, true)).data; 161 | if (data) { 162 | arr.push("群号:" + data.group_id); 163 | arr.push("群名:" + data.group_name); 164 | arr.push(`人数:${data.member_count}/${data.max_member_count}`); 165 | arr.push("等级:" + data.grade); 166 | arr.push("活跃人数:" + data.active_member_count); 167 | arr.push("创建时间:" + new Date(data.create_time * 1000)); 168 | } 169 | } 170 | vscode.window.showQuickPick(arr); 171 | }); 172 | 173 | vscode.commands.registerCommand("oicq.friend.search", () => { 174 | if (!firendTreeView) { 175 | return vscode.window.showErrorMessage("请先登录"); 176 | } 177 | const arr = []; 178 | for (let [k, v] of client.fl) { 179 | arr.push(v.nickname + " (" + v.user_id + ")"); 180 | } 181 | vscode.window.showQuickPick(arr).then((value) => { 182 | if (value) { 183 | const uin = value.slice(value.lastIndexOf("(") + 1, -1); 184 | firendTreeView.reveal(genContactId("u", Number(uin)), { focus: true, select: true }); 185 | } 186 | }); 187 | }); 188 | vscode.commands.registerCommand("oicq.group.search", () => { 189 | if (!groupTreeView) { 190 | return vscode.window.showErrorMessage("请先登录"); 191 | } 192 | const arr = []; 193 | for (let [k, v] of client.gl) { 194 | arr.push(v.group_name + " (" + v.group_id + ")"); 195 | } 196 | vscode.window.showQuickPick(arr).then((value) => { 197 | if (value) { 198 | const uin = value.slice(value.lastIndexOf("(") + 1, -1); 199 | groupTreeView.reveal(genContactId("g", Number(uin)), { focus: true, select: true }); 200 | } 201 | }); 202 | }); 203 | 204 | vscode.commands.registerCommand("oicq.group.invite", (id: string) => { 205 | const { uin, type } = parseContactId(id); 206 | let placeHolder: string, gid: number, uid: number; 207 | const arr = []; 208 | if (type === "u") { 209 | uid = uin; 210 | for (let [k, v] of client.gl) { 211 | arr.push("👨‍👦‍👦" + v.group_name + " (" + v.group_id + ")"); 212 | } 213 | placeHolder = "选择一个群,邀请好友 " + itemMap.get(id)?.tooltip + " 加入"; 214 | } else { 215 | gid = uin; 216 | for (let [k, v] of client.fl) { 217 | arr.push(v.nickname + " (" + v.user_id + ")"); 218 | } 219 | placeHolder = "选择好友,邀请TA加入群 " + itemMap.get(id)?.tooltip; 220 | } 221 | vscode.window.showQuickPick(arr, { placeHolder }).then((value) => { 222 | if (value) { 223 | const uin = value.slice(value.lastIndexOf("(") + 1, -1); 224 | if (!gid) { 225 | gid = Number(uin); 226 | } 227 | if (!uid) { 228 | uid = Number(uin); 229 | } 230 | client.inviteFriend(gid, uid).then((data) => { 231 | if (data.retcode === 0) { 232 | vscode.window.showInformationMessage("邀请发送成功。"); 233 | } else { 234 | vscode.window.showErrorMessage("邀请失败,请确认你是否有邀请的权限,或对方已经入群。"); 235 | } 236 | }); 237 | } 238 | }); 239 | }); 240 | 241 | vscode.commands.registerCommand("oicq.friend.delete", (id: string) => { 242 | vscode.window.showInformationMessage(`确定要删除好友 ${itemMap.get(id)?.tooltip} ?`, "仅删除", "删除并拉黑") 243 | .then((value) => { 244 | const { uin } = parseContactId(id); 245 | if (value === "仅删除") { 246 | client.deleteFriend(uin, false); 247 | } else if (value === "删除并拉黑") { 248 | client.deleteFriend(uin, true); 249 | } 250 | }); 251 | }); 252 | vscode.commands.registerCommand("oicq.group.delete", (id: string) => { 253 | vscode.window.showInformationMessage(`确定要退出群 ${itemMap.get(id)?.tooltip} ?`, "是") 254 | .then((value) => { 255 | const { uin } = parseContactId(id); 256 | if (value === "是") { 257 | client.setGroupLeave(uin).then(() => { 258 | setTimeout(() => { 259 | groupListTreeDataProvider.refresh(); 260 | pinnedTreeDataProvider.refresh(); 261 | }, 500); 262 | }); 263 | } 264 | }); 265 | }); 266 | 267 | vscode.commands.registerCommand("oicq.pinned.refresh", () => { 268 | pinnedTreeDataProvider?.refresh(); 269 | }); 270 | vscode.commands.registerCommand("oicq.friends.refresh", () => { 271 | friendListTreeDataProvider?.refresh(); 272 | }); 273 | vscode.commands.registerCommand("oicq.groups.refresh", () => { 274 | groupListTreeDataProvider?.refresh(); 275 | }); 276 | 277 | /** 278 | * system.online 279 | */ 280 | export async function initLists() { 281 | itemMap = new Map; 282 | friendListTreeDataProvider = new FriendListTreeDataProvider; 283 | vscode.window.registerTreeDataProvider("chat-friends", friendListTreeDataProvider); 284 | groupListTreeDataProvider = new GroupListTreeDataProvider; 285 | vscode.window.registerTreeDataProvider("chat-groups", groupListTreeDataProvider); 286 | pinnedTreeDataProvider = new PinnedTreeDataProvider; 287 | vscode.window.registerTreeDataProvider("chat-pinned", pinnedTreeDataProvider); 288 | firendTreeView = vscode.window.createTreeView("chat-friends", { 289 | treeDataProvider: friendListTreeDataProvider 290 | }); 291 | firendTreeView.reveal(""); 292 | groupTreeView = vscode.window.createTreeView("chat-groups", { 293 | treeDataProvider: groupListTreeDataProvider 294 | }); 295 | groupTreeView.reveal(""); 296 | 297 | if (!client.listenerCount("notice.friend.increase")) { 298 | client.on("notice.friend.increase", function (data) { 299 | vscode.window.showInformationMessage(`新增了好友:${data.nickname} (${data.user_id})`); 300 | friendListTreeDataProvider.refresh(); 301 | pinnedTreeDataProvider.refresh(); 302 | }); 303 | 304 | client.on("notice.friend.decrease", function (data) { 305 | vscode.window.showInformationMessage(`删除了好友:${data.nickname} (${data.user_id})`); 306 | friendListTreeDataProvider.refresh(); 307 | pinnedTreeDataProvider.refresh(); 308 | }); 309 | 310 | client.on("notice.friend.profile", function (data) { 311 | const id = genContactId("u", data.user_id); 312 | friendListTreeDataProvider.refresh(id); 313 | pinnedTreeDataProvider.refresh(id); 314 | }); 315 | 316 | client.on("notice.group.increase", function (data) { 317 | if (data.user_id === this.uin) { 318 | vscode.window.showInformationMessage(`你已加入群:${this.gl.get(data.group_id)?.group_name} (${data.group_id})`); 319 | groupListTreeDataProvider.refresh(); 320 | pinnedTreeDataProvider.refresh(); 321 | } 322 | }); 323 | client.on("notice.group.decrease", function (data) { 324 | if (data.user_id === this.uin) { 325 | let msg: string; 326 | const label = itemMap.get(genContactId("g", data.group_id))?.tooltip; 327 | if (data.dismiss) { 328 | msg = label + ` 已解散`; 329 | } else if (data.operator_id === this.uin) { 330 | msg = `你退出了群 ` + label; 331 | } else { 332 | msg = `${data.operator_id} 将你踢出了群 ` + label; 333 | } 334 | vscode.window.showInformationMessage(msg); 335 | groupListTreeDataProvider.refresh(); 336 | pinnedTreeDataProvider.refresh(); 337 | } 338 | }); 339 | 340 | client.on("notice.group.setting", function (data) { 341 | if (data.group_name) { 342 | const id = genContactId("g", data.group_id); 343 | groupListTreeDataProvider.refresh(id); 344 | pinnedTreeDataProvider.refresh(id); 345 | } 346 | }); 347 | 348 | client.on("notice.group.transfer", function (data) { 349 | if (data.user_id === this.uin) { 350 | const label = itemMap.get(genContactId("g", data.group_id))?.tooltip; 351 | const msg = `${label} 群主已将群主身份转让给你`; 352 | vscode.window.showInformationMessage(msg); 353 | } 354 | }); 355 | 356 | client.on("notice.group.admin", function (data) { 357 | if (data.user_id === this.uin) { 358 | const label = itemMap.get(genContactId("g", data.group_id))?.tooltip; 359 | const msg = data.set ? `你已成为群 ${label} 的管理员` : `你被取消了群 ${label} 的管理员`; 360 | vscode.window.showInformationMessage(msg); 361 | } 362 | }); 363 | 364 | client.on("request.friend.add", function (data) { 365 | vscode.window.showInformationMessage(`${data.nickname}(${data.user_id}) 请求添加你为好友,来自 ${data.source}。附加信息:${data.comment}`, "同意", "拒绝", "拒绝并拉黑") 366 | .then((value) => { 367 | if (value === "同意") { 368 | this.setFriendAddRequest(data.flag); 369 | } else if (value = "拒绝") { 370 | this.setFriendAddRequest(data.flag, false); 371 | } else if (value = "拒绝并拉黑") { 372 | this.setFriendAddRequest(data.flag, false, "", true); 373 | } 374 | }); 375 | }); 376 | 377 | client.on("request.group.invite", function (data) { 378 | vscode.window.showInformationMessage(`${data.nickname}(${data.user_id}) 邀请你加入群 ${data.group_name}(${data.group_id})。`, "同意", "拒绝", "拒绝并拉黑") 379 | .then((value) => { 380 | if (value === "同意") { 381 | this.setGroupAddRequest(data.flag); 382 | } else if (value = "拒绝") { 383 | this.setGroupAddRequest(data.flag, false); 384 | } else if (value = "拒绝并拉黑") { 385 | this.setGroupAddRequest(data.flag, false, "", true); 386 | } 387 | }); 388 | }); 389 | 390 | client.on("request.group.add", function (data) { 391 | // @ts-ignore 392 | if (!this.config.show_me_add_group_request) { 393 | return; 394 | } 395 | vscode.window.showInformationMessage(`${data.nickname}(${data.user_id}) 申请加入群 ${data.group_name}(${data.group_id})。附加信息:${data.comment}`, "同意", "拒绝", "拒绝并拉黑") 396 | .then((value) => { 397 | if (value === "同意") { 398 | this.setGroupAddRequest(data.flag); 399 | } else if (value = "拒绝") { 400 | this.setGroupAddRequest(data.flag, false); 401 | } else if (value = "拒绝并拉黑") { 402 | this.setGroupAddRequest(data.flag, false, "", true); 403 | } 404 | }); 405 | }); 406 | 407 | chat.bind(); 408 | } 409 | } 410 | 411 | /** 412 | * 刷新treeItems 413 | * @param flag true:有新消息 false:解除新消息 414 | */ 415 | export function refreshContacts(id: string, flag: boolean) { 416 | const item = itemMap.get(id); 417 | const { type } = parseContactId(id); 418 | const provider = type === "u" ? friendListTreeDataProvider : groupListTreeDataProvider; 419 | if (!item) { 420 | provider.refresh(); 421 | pinnedTreeDataProvider.refresh(); 422 | return; 423 | } 424 | if (flag) { 425 | ++item.new; 426 | if (item.new > 1) { 427 | provider.refresh(id); 428 | pinnedTreeDataProvider.refresh(id); 429 | } else { 430 | provider.refresh(); 431 | pinnedTreeDataProvider.refresh(id); 432 | } 433 | } else { 434 | if (!item.new) { 435 | return; 436 | } else { 437 | item.new = 0; 438 | provider.refresh(id); 439 | pinnedTreeDataProvider.refresh(id); 440 | } 441 | } 442 | } 443 | 444 | /** 445 | * 列表刷新规则: 446 | * 447 | * 好友增加减少时:全部刷新 448 | * 群增加减少时:全部刷新 449 | * 好友修改昵称时:单个刷新 450 | * 群名变更时:单个刷新 451 | * 452 | * 收到新消息时: 453 | * 列表中不存在时:全部刷新 454 | * 之前无新消息时:全部刷新(新消息+1) 455 | * 之前有新消息时:单个刷新(新消息+1) 456 | * 视图已打开并可见时:不刷新 457 | * 视图已打开不可见时: 458 | * 之前无新消息时:全部刷新(新消息+1) 459 | * 之前有新消息时:单个刷新(新消息+1) 460 | * 461 | * 打开视图时/视图聚焦时: 462 | * 之前无新消息时:不刷新 463 | * 之前有新消息时:单个刷新(新消息置0) 464 | * 465 | * (全部刷新的目的在于排序) 466 | */ 467 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // The module 'vscode' contains the VS Code extensibility API 2 | // Import the module and reference it with the alias vscode in your code below 3 | import * as vscode from 'vscode'; 4 | import * as fs from 'fs'; 5 | import * as path from 'path'; 6 | import * as global from "./global"; 7 | import * as client from "./client"; 8 | 9 | let timer: NodeJS.Timeout | undefined; 10 | 11 | // this method is called when your extension is activated 12 | // your extension is activated the very first time the command is executed 13 | export function activate(context: vscode.ExtensionContext) { 14 | 15 | // create work dir 16 | global.setContext(context); 17 | if (!fs.existsSync(context.globalStoragePath)) { 18 | fs.mkdirSync(context.globalStoragePath); 19 | } 20 | 21 | if (!timer) { 22 | timer = setInterval(() => { 23 | if (global.client?.isOnline()) { 24 | fs.writeFile(path.join(global.client.dir, "online.lock"), "114514", () => { }); 25 | } 26 | }, 5000); 27 | } 28 | 29 | // creat status bar item 30 | const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); 31 | statusBarItem.text = "QQ"; 32 | statusBarItem.command = "oicq.statusBar.click"; 33 | statusBarItem.show(); 34 | vscode.commands.registerCommand("oicq.statusBar.click", client.invoke); 35 | } 36 | 37 | // this method is called when your extension is deactivated 38 | export function deactivate() { 39 | if (timer) { 40 | clearInterval(timer); 41 | timer = undefined; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/global.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import * as oicq from 'oicq'; 3 | 4 | interface ContactId { 5 | self: number, 6 | type: "u" | "g", 7 | uin: number, 8 | } 9 | 10 | function genContactId(type: "u" | "g", uin: number): string { 11 | return client.uin + type + uin; 12 | } 13 | 14 | function parseContactId(id: string): ContactId { 15 | let type: ContactId["type"]; 16 | if (id.includes("u")) { 17 | type = "u"; 18 | } else { 19 | type = "g"; 20 | } 21 | let self = parseInt(id.split(type)[0]); 22 | let uin = parseInt(id.split(type)[1]); 23 | return { self, type, uin }; 24 | } 25 | 26 | function setContext(context: vscode.ExtensionContext) { 27 | ctx = context; 28 | } 29 | 30 | function setClient(c: oicq.Client) { 31 | client = c; 32 | } 33 | 34 | let ctx: vscode.ExtensionContext; 35 | let client: oicq.Client; 36 | 37 | const NOOP = () => { }; 38 | 39 | export { ctx, client, setContext, setClient, NOOP, genContactId, parseContactId }; 40 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from 'vscode-test'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests'); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 13 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es2019", 5 | "outDir": "out", 6 | "lib": [ 7 | "es2019", 8 | "dom" 9 | ], 10 | "sourceMap": true, 11 | "rootDir": "src", 12 | "strict": true /* enable all strict type-checking options */ 13 | /* Additional Checks */ 14 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 15 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 16 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 17 | }, 18 | "exclude": [ 19 | "node_modules", 20 | ".vscode-test" 21 | ] 22 | } 23 | --------------------------------------------------------------------------------