├── .editorconfig
├── .gitignore
├── Attributes
└── DemoBeforeCommandAttribute.cs
├── Middleware
└── TestMiddleware.cs
├── Models
└── Duration.cs
├── Modules
└── TestModule.cs
├── OneBot-Framework-Demo.csproj
├── OneBot-Framework-Demo.sln
├── Program.cs
├── Properties
└── launchSettings.json
├── README.md
├── appsettings-reverse_ws-example.Development.json
├── appsettings-reverse_ws-example.json
├── appsettings-ws-example.Development.json
└── appsettings-ws-example.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 |
6 | indent_style = space
7 | indent_size = 4
8 |
9 | insert_final_newline = true;
10 |
11 | spaces_around_operators = true;
12 |
13 | end_of_line = lf;
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### VisualStudio template
3 | ## Ignore Visual Studio temporary files, build results, and
4 | ## files generated by popular Visual Studio add-ons.
5 | ##
6 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
7 |
8 | # User-specific files
9 | *.rsuser
10 | *.suo
11 | *.user
12 | *.userosscache
13 | *.sln.docstates
14 |
15 | # User-specific files (MonoDevelop/Xamarin Studio)
16 | *.userprefs
17 |
18 | # Mono auto generated files
19 | mono_crash.*
20 |
21 | # Build results
22 | [Dd]ebug/
23 | [Dd]ebugPublic/
24 | [Rr]elease/
25 | [Rr]eleases/
26 | x64/
27 | x86/
28 | [Ww][Ii][Nn]32/
29 | [Aa][Rr][Mm]/
30 | [Aa][Rr][Mm]64/
31 | bld/
32 | [Bb]in/
33 | [Oo]bj/
34 | [Ll]og/
35 | [Ll]ogs/
36 |
37 | # Visual Studio 2015/2017 cache/options directory
38 | .vs/
39 | # Uncomment if you have tasks that create the project's static files in wwwroot
40 | #wwwroot/
41 |
42 | # IDEA cache/options directory
43 | .idea/
44 |
45 | # Visual Studio 2017 auto generated files
46 | Generated\ Files/
47 |
48 | # MSTest test Results
49 | [Tt]est[Rr]esult*/
50 | [Bb]uild[Ll]og.*
51 |
52 | # NUnit
53 | *.VisualState.xml
54 | TestResult.xml
55 | nunit-*.xml
56 |
57 | # Build Results of an ATL Project
58 | [Dd]ebugPS/
59 | [Rr]eleasePS/
60 | dlldata.c
61 |
62 | # Benchmark Results
63 | BenchmarkDotNet.Artifacts/
64 |
65 | # .NET Core
66 | project.lock.json
67 | project.fragment.lock.json
68 | artifacts/
69 |
70 | # ASP.NET Scaffolding
71 | ScaffoldingReadMe.txt
72 |
73 | # StyleCop
74 | StyleCopReport.xml
75 |
76 | # Files built by Visual Studio
77 | *_i.c
78 | *_p.c
79 | *_h.h
80 | *.ilk
81 | *.meta
82 | *.obj
83 | *.iobj
84 | *.pch
85 | *.pdb
86 | *.ipdb
87 | *.pgc
88 | *.pgd
89 | *.rsp
90 | *.sbr
91 | *.tlb
92 | *.tli
93 | *.tlh
94 | *.tmp
95 | *.tmp_proj
96 | *_wpftmp.csproj
97 | *.log
98 | *.vspscc
99 | *.vssscc
100 | .builds
101 | *.pidb
102 | *.svclog
103 | *.scc
104 |
105 | # Chutzpah Test files
106 | _Chutzpah*
107 |
108 | # Visual C++ cache files
109 | ipch/
110 | *.aps
111 | *.ncb
112 | *.opendb
113 | *.opensdf
114 | *.sdf
115 | *.cachefile
116 | *.VC.db
117 | *.VC.VC.opendb
118 |
119 | # Visual Studio profiler
120 | *.psess
121 | *.vsp
122 | *.vspx
123 | *.sap
124 |
125 | # Visual Studio Trace Files
126 | *.e2e
127 |
128 | # TFS 2012 Local Workspace
129 | $tf/
130 |
131 | # Guidance Automation Toolkit
132 | *.gpState
133 |
134 | # ReSharper is a .NET coding add-in
135 | _ReSharper*/
136 | *.[Rr]e[Ss]harper
137 | *.DotSettings.user
138 |
139 | # TeamCity is a build add-in
140 | _TeamCity*
141 |
142 | # DotCover is a Code Coverage Tool
143 | *.dotCover
144 |
145 | # AxoCover is a Code Coverage Tool
146 | .axoCover/*
147 | !.axoCover/settings.json
148 |
149 | # Coverlet is a free, cross platform Code Coverage Tool
150 | coverage*[.json, .xml, .info]
151 |
152 | # Visual Studio code coverage results
153 | *.coverage
154 | *.coveragexml
155 |
156 | # NCrunch
157 | _NCrunch_*
158 | .*crunch*.local.xml
159 | nCrunchTemp_*
160 |
161 | # MightyMoose
162 | *.mm.*
163 | AutoTest.Net/
164 |
165 | # Web workbench (sass)
166 | .sass-cache/
167 |
168 | # Installshield output folder
169 | [Ee]xpress/
170 |
171 | # DocProject is a documentation generator add-in
172 | DocProject/buildhelp/
173 | DocProject/Help/*.HxT
174 | DocProject/Help/*.HxC
175 | DocProject/Help/*.hhc
176 | DocProject/Help/*.hhk
177 | DocProject/Help/*.hhp
178 | DocProject/Help/Html2
179 | DocProject/Help/html
180 |
181 | # Click-Once directory
182 | publish/
183 |
184 | # Publish Web Output
185 | *.[Pp]ublish.xml
186 | *.azurePubxml
187 | # Note: Comment the next line if you want to checkin your web deploy settings,
188 | # but database connection strings (with potential passwords) will be unencrypted
189 | *.pubxml
190 | *.publishproj
191 |
192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
193 | # checkin your Azure Web App publish settings, but sensitive information contained
194 | # in these scripts will be unencrypted
195 | PublishScripts/
196 |
197 | # NuGet Packages
198 | *.nupkg
199 | # NuGet Symbol Packages
200 | *.snupkg
201 | # The packages folder can be ignored because of Package Restore
202 | **/[Pp]ackages/*
203 | # except build/, which is used as an MSBuild target.
204 | !**/[Pp]ackages/build/
205 | # Uncomment if necessary however generally it will be regenerated when needed
206 | #!**/[Pp]ackages/repositories.config
207 | # NuGet v3's project.json files produces more ignorable files
208 | *.nuget.props
209 | *.nuget.targets
210 |
211 | # Microsoft Azure Build Output
212 | csx/
213 | *.build.csdef
214 |
215 | # Microsoft Azure Emulator
216 | ecf/
217 | rcf/
218 |
219 | # Windows Store app package directories and files
220 | AppPackages/
221 | BundleArtifacts/
222 | Package.StoreAssociation.xml
223 | _pkginfo.txt
224 | *.appx
225 | *.appxbundle
226 | *.appxupload
227 |
228 | # Visual Studio cache files
229 | # files ending in .cache can be ignored
230 | *.[Cc]ache
231 | # but keep track of directories ending in .cache
232 | !?*.[Cc]ache/
233 |
234 | # Others
235 | ClientBin/
236 | ~$*
237 | *~
238 | *.dbmdl
239 | *.dbproj.schemaview
240 | *.jfm
241 | *.pfx
242 | *.publishsettings
243 | orleans.codegen.cs
244 |
245 | # Including strong name files can present a security risk
246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
247 | #*.snk
248 |
249 | # Since there are multiple workflows, uncomment next line to ignore bower_components
250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
251 | #bower_components/
252 |
253 | # RIA/Silverlight projects
254 | Generated_Code/
255 |
256 | # Backup & report files from converting an old project file
257 | # to a newer Visual Studio version. Backup files are not needed,
258 | # because we have git ;-)
259 | _UpgradeReport_Files/
260 | Backup*/
261 | UpgradeLog*.XML
262 | UpgradeLog*.htm
263 | ServiceFabricBackup/
264 | *.rptproj.bak
265 |
266 | # SQL Server files
267 | *.mdf
268 | *.ldf
269 | *.ndf
270 |
271 | # Business Intelligence projects
272 | *.rdl.data
273 | *.bim.layout
274 | *.bim_*.settings
275 | *.rptproj.rsuser
276 | *- [Bb]ackup.rdl
277 | *- [Bb]ackup ([0-9]).rdl
278 | *- [Bb]ackup ([0-9][0-9]).rdl
279 |
280 | # Microsoft Fakes
281 | FakesAssemblies/
282 |
283 | # GhostDoc plugin setting file
284 | *.GhostDoc.xml
285 |
286 | # Node.js Tools for Visual Studio
287 | .ntvs_analysis.dat
288 | node_modules/
289 |
290 | # Visual Studio 6 build log
291 | *.plg
292 |
293 | # Visual Studio 6 workspace options file
294 | *.opt
295 |
296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
297 | *.vbw
298 |
299 | # Visual Studio LightSwitch build output
300 | **/*.HTMLClient/GeneratedArtifacts
301 | **/*.DesktopClient/GeneratedArtifacts
302 | **/*.DesktopClient/ModelManifest.xml
303 | **/*.Server/GeneratedArtifacts
304 | **/*.Server/ModelManifest.xml
305 | _Pvt_Extensions
306 |
307 | # Paket dependency manager
308 | .paket/paket.exe
309 | paket-files/
310 |
311 | # FAKE - F# Make
312 | .fake/
313 |
314 | # CodeRush personal settings
315 | .cr/personal
316 |
317 | # Python Tools for Visual Studio (PTVS)
318 | __pycache__/
319 | *.pyc
320 |
321 | # Cake - Uncomment if you are using it
322 | # tools/**
323 | # !tools/packages.config
324 |
325 | # Tabs Studio
326 | *.tss
327 |
328 | # Telerik's JustMock configuration file
329 | *.jmconfig
330 |
331 | # BizTalk build output
332 | *.btp.cs
333 | *.btm.cs
334 | *.odx.cs
335 | *.xsd.cs
336 |
337 | # OpenCover UI analysis results
338 | OpenCover/
339 |
340 | # Azure Stream Analytics local run output
341 | ASALocalRun/
342 |
343 | # MSBuild Binary and Structured Log
344 | *.binlog
345 |
346 | # NVidia Nsight GPU debugger configuration file
347 | *.nvuser
348 |
349 | # MFractors (Xamarin productivity tool) working folder
350 | .mfractor/
351 |
352 | # Local History for Visual Studio
353 | .localhistory/
354 |
355 | # BeatPulse healthcheck temp database
356 | healthchecksdb
357 |
358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
359 | MigrationBackup/
360 |
361 | # Ionide (cross platform F# VS Code tools) working folder
362 | .ionide/
363 |
364 | #
365 | appsettings.json
366 | appsettings.Development.json
--------------------------------------------------------------------------------
/Attributes/DemoBeforeCommandAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using OneBot.CommandRoute.Attributes;
4 | using OneBot.CommandRoute.Models;
5 | using Sora.Entities.Info;
6 | using Sora.Enumeration.ApiType;
7 | using Sora.Enumeration.EventParamsType;
8 | using Sora.EventArgs.SoraEvent;
9 |
10 | namespace OneBot.FrameworkDemo.Attributes;
11 |
12 | ///
13 | /// 在触发指令前的切片
14 | ///
15 | public class DemoBeforeCommandAttribute : BeforeCommandAttribute
16 | {
17 | public override void Invoke(OneBotContext context)
18 | {
19 | IServiceScope scope = context.ServiceScope;
20 | BaseSoraEventArgs baseSoraEventArgs = context.SoraEventArgs;
21 |
22 | if (baseSoraEventArgs is not GroupMessageEventArgs p) return;
23 |
24 | var taskValue = p.SoraApi.GetGroupMemberInfo(p.SourceGroup, p.Sender.Id, true).AsTask();
25 | taskValue.Wait();
26 |
27 | if (taskValue.Result.apiStatus.RetCode != ApiStatusType.Ok)
28 | {
29 | return;
30 | }
31 |
32 | GroupMemberInfo uInfo = taskValue.Result.memberInfo;
33 |
34 | if (uInfo.Role != MemberRoleType.Member)
35 | {
36 | Console.WriteLine($"发送者 {uInfo.UserId} 是管理员");
37 | }
38 | else
39 | {
40 | Console.WriteLine($"发送者 {uInfo.UserId} 不是管理员");
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/Middleware/TestMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.Extensions.Logging;
3 | using OneBot.CommandRoute.Models;
4 | using OneBot.CommandRoute.Services;
5 |
6 | namespace OneBot.FrameworkDemo.Middleware;
7 |
8 | public class TestMiddleware: IOneBotMiddleware
9 | {
10 | private ILogger _logger;
11 |
12 | public TestMiddleware(ILogger logger)
13 | {
14 | _logger = logger;
15 | }
16 |
17 | public ValueTask Invoke(OneBotContext oneBotContext, OneBotRequestDelegate next)
18 | {
19 | using (_logger.BeginScope("机器人开始处理东西了")) {
20 | return next(oneBotContext);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Models/Duration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 |
4 | namespace OneBot.FrameworkDemo.Models;
5 |
6 | ///
7 | /// 时长类型;
8 | /// 这里是示例自定义参数类型;
9 | /// 需实现一个从 String 或 CQCode 类型来的隐式转换函数才能触发类型解析。
10 | ///
11 | public class Duration
12 | {
13 | ///
14 | /// 时长
15 | ///
16 | public long Seconds { get; private set; }
17 | public Duration(long seconds)
18 | {
19 | Seconds = seconds;
20 | }
21 |
22 | ///
23 | /// 实现一个从数字到 Duration 的隐式转换
24 | ///
25 | /// 时长
26 | public static implicit operator Duration(int seconds)
27 | {
28 | if (seconds < 0)
29 | {
30 | throw new ArgumentException($"无法解析参数。 {seconds}");
31 | }
32 |
33 | return new Duration(seconds);
34 | }
35 |
36 | ///
37 | /// 实现一个从数字到 Duration 的隐式转换
38 | ///
39 | /// 时长
40 | public static implicit operator Duration(long seconds)
41 | {
42 | if (seconds < 0)
43 | {
44 | throw new ArgumentException($"无法解析参数。 {seconds}");
45 | }
46 |
47 | return new Duration(seconds);
48 | }
49 |
50 | ///
51 | /// 实现一个从字符串到 Duration 的隐式转换。
52 | /// 1d2h3m4s -> 1天2小时3分钟4秒
53 | ///
54 | /// 时长
55 | public static implicit operator Duration(string value)
56 | {
57 | if (!CanParse(value))
58 | {
59 | throw new ArgumentException($"无法解析参数。 {value}");
60 | }
61 |
62 | var s = "";
63 | long ret = 0;
64 | for (var i = 0; i < value.Length; i++)
65 | {
66 | if ((value[i] == 's') || (value[i] == 'S'))
67 | {
68 | ret += Convert.ToInt64(s);
69 | s = "";
70 | continue;
71 | }
72 |
73 | if ((value[i] == 'm') || (value[i] == 'M'))
74 | {
75 | ret += Convert.ToInt64(s) * 60;
76 | s = "";
77 | continue;
78 | }
79 |
80 | if ((value[i] == 'h') || (value[i] == 'H'))
81 | {
82 | ret += Convert.ToInt64(s) * 3600;
83 | s = "";
84 | continue;
85 | }
86 |
87 | if ((value[i] == 'd') || (value[i] == 'D'))
88 | {
89 | ret += Convert.ToInt64(s) * 86400;
90 | s = "";
91 | continue;
92 | }
93 |
94 | s += value[i];
95 | }
96 | return new Duration(ret);
97 | }
98 |
99 | ///
100 | /// 判断能否解析
101 | ///
102 | ///
103 | ///
104 | public static bool CanParse(string s)
105 | {
106 | return s != null && Regex.IsMatch(s, @"^(\d*d)?(\d*h)?(\d*m)?(\d*s)?$");
107 | }
108 |
109 | ///
110 | /// 尝试解析
111 | ///
112 | ///
113 | ///
114 | ///
115 | public static bool TryParse(string s, out Duration duration)
116 | {
117 | duration = null;
118 | if (s == null) return false;
119 | if (long.TryParse(s, out long number))
120 | {
121 | duration = number;
122 | }
123 | else if (Regex.IsMatch(s, @"^(\d*d)?(\d*h)?(\d*m)?(\d*s)?$"))
124 | {
125 | duration = s;
126 | }
127 | else return false;
128 | return true;
129 | }
130 |
131 | ///
132 | /// 返回可用 ^(\d*d)?(\d*h)?(\d*m)?(\d*s)?$ 匹配的最简时长表达式
133 | ///
134 | ///
135 | public override string ToString()
136 | {
137 | var temp = Seconds;
138 | var ret = "";
139 | var day = temp / 86400;
140 | if (day > 0) ret += $"{day}d";
141 | temp %= 86400;
142 | var hour = temp / 3600;
143 | if (hour > 0) ret += $"{hour}h";
144 | temp %= 3600;
145 | var min = temp / 60;
146 | if (min > 0) ret += $"{min}m";
147 | temp %= 60;
148 | if (temp > 0) ret += $"{temp}s";
149 | return ret;
150 | }
151 | }
--------------------------------------------------------------------------------
/Modules/TestModule.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.Extensions.Logging;
3 | using OneBot.CommandRoute.Attributes;
4 | using OneBot.CommandRoute.Models.Enumeration;
5 | using OneBot.CommandRoute.Services;
6 | using OneBot.FrameworkDemo.Attributes;
7 | using OneBot.FrameworkDemo.Models;
8 | using Sora.Entities;
9 | using Sora.EventArgs.SoraEvent;
10 |
11 | namespace OneBot.FrameworkDemo.Modules;
12 |
13 | ///
14 | /// 这个是示例处理代码,所有的 OneBot 处理对象均需实现 IOneBotController 接口
15 | ///
16 | public class TestModule : IOneBotController
17 | {
18 | private readonly ILogger _logger;
19 |
20 | public TestModule(ICommandService commandService, ILogger logger)
21 | {
22 | // 通过构造函数获得指令路由服务对象
23 |
24 | // 基本事件处理例子
25 | // 如果你不想要指令路由,可以使用这个方法来注册最原始的事件监听方法
26 | commandService.Event.OnGroupMessage += (scope) =>
27 | {
28 | var args = scope.WrapSoraEventArgs();
29 |
30 | // 在控制台中复读群里的信息
31 | logger.LogInformation($"{args.SourceGroup.Id} : {args.Sender.Id} : {args.Message.RawText}");
32 |
33 | return 0;
34 | // 这里返回 0,表示继续传递该事件给后续的指令或监听。
35 | };
36 |
37 | // 全局异常处理事件
38 | commandService.Event.OnException += (scope, exception) =>
39 | {
40 | logger.LogError($"{exception.Message}");
41 | };
42 |
43 | _logger = logger;
44 | }
45 |
46 | ///
47 | /// 指令定义例子
48 | ///
49 | /// 定义一个指令:
50 | /// 使用 < > 括起来的是必选参数;
51 | /// 使用 [ ] 括起来的是可选参数
52 | ///
53 | /// 禁言时长
54 | /// 全参数列表
55 | /// 被禁言用户
56 | /// 原始事件信息
57 | [Command("mute [duration]", Alias = new[] {"禁言 [duration]", "口球 [duration]"}, EventType = EventType.GroupMessage)]
58 | [DemoBeforeCommand]
59 | public void MuteInGroupWithDuration(Duration duration, [ParsedArguments] object[] args, [CommandParameter("uid")] User userInfo, GroupMessageEventArgs e)
60 | {
61 | duration ??= new Duration(600);
62 | _logger.LogInformation($"{e.Sender.Id} 使用指令禁言 {userInfo.Id} 用户 {duration.Seconds} 秒。");
63 |
64 | // 这个指令没有返回值,其隐式返回 1,在执行完毕后不再传递该事件给后续的指令或监听。
65 | }
66 |
67 | ///
68 | /// 再定义一个指令:
69 | ///
70 | /// 参数不必要按顺序、参数会触发依赖注入
71 | ///
72 | /// 操作群号
73 | /// 被禁言用户
74 | /// 禁言时长
75 | /// 原始事件信息
76 |
77 | [Command("mute [duration]", Alias = new[] {"禁言 [duration]", "口球 [duration]"}, EventType = EventType.GroupMessage | EventType.PrivateMessage)]
78 | public int MuteInGroupWithGroupId(Group gid, User uid, Duration duration, BaseSoraEventArgs e)
79 | {
80 | duration ??= new Duration(600);
81 |
82 | switch (e)
83 | {
84 | case GroupMessageEventArgs s1:
85 | _logger.LogInformation($"{s1.Sender.Id} 使用指令禁言 {gid.Id} 群里的 {uid.Id} 用户 {duration.Seconds} 秒。");
86 | break;
87 | case PrivateMessageEventArgs s2:
88 | _logger.LogInformation($"{s2.Sender.Id} 使用指令禁言 {gid.Id} 群里的 {uid.Id} 用户 {duration.Seconds} 秒。");
89 | break;
90 | }
91 |
92 | return 1;
93 | // 这里是显示返回 1,在执行完毕后不再传递该事件给后续的指令或监听。
94 | // return 0;
95 | // 当然也可以返回 0,表示继续传递。
96 | }
97 |
98 | ///
99 | /// 小程序简单监听方法,
100 | /// 这里是监听了群签到。
101 | ///
102 | ///
103 | [CQJson("com.tencent.qq.checkin", EventType = EventType.GroupMessage)]
104 | public void CheckInListener(GroupMessageEventArgs e)
105 | {
106 | _logger.LogInformation($"{e.Sender.Id} 签到成功!");
107 |
108 | // 当然这里也是可以返回 0 或 1 的。
109 | }
110 |
111 | ///
112 | /// 全局异常处理函数测试
113 | ///
114 | [Command("exception", EventType = EventType.GroupMessage | EventType.PrivateMessage)]
115 | public void ExceptionTest()
116 | {
117 | throw new Exception("测试全局异常处理");
118 | }
119 | }
--------------------------------------------------------------------------------
/OneBot-Framework-Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | warnings
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/OneBot-Framework-Demo.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30907.101
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OneBot-Framework-Demo", "OneBot-Framework-Demo.csproj", "{3F360C57-4065-489C-9A89-94B549E978A8}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {3F360C57-4065-489C-9A89-94B549E978A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {3F360C57-4065-489C-9A89-94B549E978A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {3F360C57-4065-489C-9A89-94B549E978A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {3F360C57-4065-489C-9A89-94B549E978A8}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {1961C81B-BAE0-461E-B51B-D2E23BB555D9}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.Hosting;
3 | using OneBot.CommandRoute.Mixin;
4 | using OneBot.CommandRoute.Models.VO;
5 | using OneBot.CommandRoute.Services;
6 | using OneBot.FrameworkDemo.Middleware;
7 | using OneBot.FrameworkDemo.Modules;
8 |
9 | namespace OneBot.FrameworkDemo;
10 |
11 | public class Program
12 | {
13 | public static void Main(string[] args)
14 | {
15 | // 创建主机
16 | var builder = Host.CreateDefaultBuilder(args);
17 |
18 | // 配置 OneBot 主机
19 | builder.ConfigureOneBotHost();
20 |
21 | // 配置服务
22 | builder.ConfigureServices((context, services) =>
23 | {
24 | var configuration = context.Configuration;
25 | // 配置机器人核心
26 | // 设置 OneBot 配置
27 | services.Configure(configuration.GetSection("CQHttpConfig"));
28 | services.ConfigureOneBot();
29 |
30 | // 添加中间件
31 | // 单例模式或原型模式都可以,问题不大。
32 | services.AddScoped();
33 |
34 | // 添加指令 / 事件
35 | // 推荐使用单例模式(而实际上框架代码也是当单例模式使用的)
36 | services.AddSingleton();
37 | // 一行一行地将指令模块加进去
38 | });
39 |
40 | // 开始运行
41 | builder.Build().Run();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:50698",
7 | "sslPort": 44310
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "OneBot-Framework-Demo": {
19 | "commandName": "Project",
20 | "environmentVariables": {
21 | "ASPNETCORE_ENVIRONMENT": "Development"
22 | },
23 | "applicationUrl": "https://localhost:5001;http://localhost:5000"
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OneBot - Command Route Project Template
2 |
3 | ## 使用说明
4 | ### 使用反向 WS
5 | 1. 克隆本项目。
6 | 2. 将 `appsettings-reverse_ws-example.Development.json` 重命名为 `appsettings.Development.json`。
7 | 3. 将 `appsettings-reverse_ws-example.json` 重命名为 `appsettings.json`。
8 | 4. 根据需要编辑配置文件中的信息。
9 | 5. 编译运行。
10 |
11 | ### 使用正向 WS
12 | 1. 克隆本项目。
13 | 2. 将 `appsettings-ws-example.Development.json` 重命名为 `appsettings.Development.json`。
14 | 3. 将 `appsettings-ws-example.json` 重命名为 `appsettings.json`。
15 | 4. 根据需要编辑配置文件中的信息。
16 | 5. 编译运行。
17 |
18 | ## 简单的功能说明
19 | ### 指令路由
20 | - 见类 `OneBot.FrameworkDemo.Modules.TestModule`。
21 | ```cs
22 | ///
23 | /// 再定义一个指令:
24 | ///
25 | /// 参数不必要按顺序、参数会触发依赖注入
26 | ///
27 | /// 操作群号
28 | /// 被禁言用户
29 | /// 禁言时长
30 | [Command("mute [duration]", Alias = "禁言 [duration], 口球 [duration],", EventType = EventType.GroupMessage | EventType.PrivateMessage)]
31 | public void MuteInGroupWithGroupId(Group gid, User uid, Duration duration)
32 | {
33 | if (duration == null) duration = new Duration(600);
34 | Console.WriteLine($"禁言 {gid.Id} 群里的 {uid.Id} 用户 {duration.Seconds} 秒。");
35 | }
36 | ```
37 | - 使用属性 `[Command]` 来将本函数定义为一个指令函数。若接收到合适的消息,本方法会被调用。
38 | - 方法参数会被服务容器进行依赖注入,注入环境为 Scope。
39 |
40 | ### 小程序信息监听
41 | - 见类 `OneBot.FrameworkDemo.Modules.TestModule`。
42 | ```cs
43 | ///
44 | /// 小程序简单监听方法,
45 | /// 这里是监听了群签到。
46 | ///
47 | ///
48 | [CQJson("com.tencent.qq.checkin", EventType = EventType.GroupMessage)]
49 | public void CheckInListener(GroupMessageEventArgs e)
50 | {
51 | _logger.LogInformation($"{e.Sender.Id} 签到成功!");
52 |
53 | // 当然这里也是可以返回 0 或 1 的。
54 | }
55 | ```
56 | - 使用属性 `[CQJson]` 来将本函数定义为一个小程序监听函数。若接收到合适的消息,本方法会被调用。
57 | - 方法参数会被服务容器进行依赖注入,注入环境为 Scope。
58 |
59 | ### 智能类型转换
60 | - 见类 `OneBot.FrameworkDemo.Models.Duration`
61 | ```cs
62 | ///
63 | /// 实现一个从字符串到 Duration 的隐式转换。
64 | /// 1d2h3m4s -> 1天2小时3分钟4秒
65 | ///
66 | /// 时长
67 | public static implicit operator Duration(string value)
68 | ```
69 | - 实现一个从 `string` 到对应的类型的隐式转换即可让指令路由系统支持这个类型的转换。
70 | - 若无法转换可抛出异常使得指令路由系统不处理该消息。
71 |
72 | ### 指令触发前事件
73 | - 见类 `OneBot.FrameworkDemo.Attributes.DemoBeforeCommandAttribute`。
74 | - - 若有需要可以自行定义一个 `BeforeCommandAttribute` 的子类来实现在解析指令后触发指令前进行拦截。(如实现权限判断和指令冷却时间等)
75 |
76 | ### 依赖注入
77 | - 每一条消息都在 `Microsoft.Extensions.DependencyInjection` 的一个 `Scope` 中。可以根据需要添加自己的服务。
78 | - 指令参数中多余的参数会被 scope 容器中的服务填充。
79 | - 指令方法的参数填充优先级:
80 | 1. 指令参数(指令定义的参数名和形参列表保持一致,若不一致可使用 `CommandParameter` 指定)。
81 | 2. 指令参数数组(`[ParsedArguments] object[] args`)。
82 | 3. 原始事件信息 `BaseSoraEventArgs`、`PrivateMessageEventArgs`、`GroupMessageEventArgs`。
83 | 4. Scope 对象 `IServiceScope`。
84 | 5. OneBot 上下文对象 `OneBotContext`。
85 | 6. 剩余的未被上述可能成功填充的参数均会被从 Scope 容器中获取的对象填充。
86 |
87 | ### 基本事件
88 | - 见类 `OneBot.FrameworkDemo.Modules.TestModule`。
89 | ```cs
90 | public TestModule(ICommandService commandService, ILogger logger)
91 | {
92 | // 通过构造函数获得指令路由服务对象
93 |
94 | // 基本事件处理例子
95 | // 如果你不想要指令路由,可以使用这个方法来注册最原始的事件监听方法
96 | commandService.Event.OnGroupMessage += (context) =>
97 | {
98 | var args = context.WrapSoraEventArgs();
99 |
100 | // 在控制台中复读群里的信息
101 | logger.LogInformation($"{args.SourceGroup.Id} : {args.Sender.Id} : {args.Message.RawText}");
102 |
103 | return 0;
104 | // 这里返回 0,表示继续传递该事件给后续的指令或监听。
105 | };
106 |
107 | // 全局异常处理事件
108 | commandService.Event.OnException += (context, exception) =>
109 | {
110 | logger.LogError($"{exception.Message}");
111 | };
112 |
113 | _logger = logger;
114 | }
115 | ```
116 | - 若不需要指令路由,依然可以通过监听事件来添加自己的处理逻辑。
117 |
118 | ### 事件拦截器
119 | - 见类 `OneBot.FrameworkDemo.Middleware.TestMiddleware`
120 |
121 | ### 消息处理链
122 | - 指令函数可以返回 `int` 类型,基本事件返回 `int` 类型。若返回值为 1 则该事件阻断,不再传递给后续指令或事件监听函数;若返回值为 0 则继续传递。
--------------------------------------------------------------------------------
/appsettings-reverse_ws-example.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "CQHttpConfig": {
10 | "Mode": "reverse_ws",
11 | "Host": "127.0.0.1",
12 | "Port": 15951,
13 | "AccessToken": "",
14 | "ApiPath": "",
15 | "EventPath": "",
16 | "UniversalPath": "",
17 | "HeartBeatTimeOut": 10,
18 | "ApiTimeOut": 1000
19 | }
20 | }
--------------------------------------------------------------------------------
/appsettings-reverse_ws-example.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "CQHttpConfig": {
10 | "Mode": "reverse_ws",
11 | "Host": "127.0.0.1",
12 | "Port": 15951,
13 | "AccessToken": "",
14 | "ApiPath": "",
15 | "EventPath": "",
16 | "UniversalPath": "",
17 | "HeartBeatTimeOut": 10,
18 | "ApiTimeOut": 1000
19 | }
20 | }
--------------------------------------------------------------------------------
/appsettings-ws-example.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "CQHttpConfig": {
10 | "Mode": "ws",
11 | "Host": "127.0.0.1",
12 | "Port": 6700,
13 | "AccessToken": "",
14 | "ApiPath": "",
15 | "EventPath": "",
16 | "UniversalPath": "",
17 | "HeartBeatTimeOut": 10,
18 | "ApiTimeOut": 1000
19 | }
20 | }
--------------------------------------------------------------------------------
/appsettings-ws-example.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "CQHttpConfig": {
10 | "Mode": "ws",
11 | "Host": "127.0.0.1",
12 | "Port": 6700,
13 | "AccessToken": "",
14 | "ApiPath": "",
15 | "EventPath": "",
16 | "UniversalPath": "",
17 | "HeartBeatTimeOut": 10,
18 | "ApiTimeOut": 1000
19 | }
20 | }
--------------------------------------------------------------------------------