B 站直播姬!定时向群友通知群主何时进行女装直播的消息。
软硬条件 名 值 IDE VS2017.5 Newbe.Mahua 1.6
业务逻辑 收到 “直播姬起飞” 的消息后,启动定时任务,每个整点时,检测 B 站直播间当前是否正在直播。
如果正在直播,就向群发送 “群主正在女装” 的消息。
收到 “直播姬降落” 的消息后,取消所有定时任务。
新建项目 使用Newbe.Mahua.Plugins.Template
模板创建项目,项目名称为Newbe.Mahua.Samples.LiveGirl
。
新建项目的详细细节,可以参照右侧链接内容:新建项目
业务逻辑实现 定义直播姬接口ILiveGirl
,包含 “启动” 和 “停止” 两个基础方法。以便收到消息命令后对定时任务进行启停。
为了提升多核 CPU 的利用率,相关接口都采用异步的方式进行定义。实际上时为了让新手看不懂
业务接口代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 using System.Threading.Tasks;namespace Newbe.Mahua.Samples.LiveGirl.Services { public interface ILivegirl { Task StartAsync () ; Task StopAsnyc () ; } }
在MahuaEvents
下添加”好友消息接收事件”,并在事件内调用业务逻辑。实现代码如下:
MahuaEvents 文件夹是本 SDK 建议将事件放置的文件夹位置。也可以不接受建议而添加在其他地方。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 using Newbe.Mahua.MahuaEvents;using Newbe.Mahua.Samples.LiveGirl.Services;namespace Newbe.Mahua.Samples.LiveGirl.MahuaEvents { public class PrivateMessageFromFriendReceivedMahuaEvent : IPrivateMessageFromFriendReceivedMahuaEvent { private readonly ILivegirl _livegirl; public PrivateMessageFromFriendReceivedMahuaEvent ( ILivegirl livegirl ) { _livegirl = livegirl; } public void ProcessFriendMessage (PrivateMessageFromFriendReceivedContext context ) { if (context.FromQq == "472158246" ) { switch (context.Message) { case "直播姬起飞" : _livegirl.StartAsync().GetAwaiter().GetResult(); break ; case "直播姬降落" : _livegirl.StopAsnyc().GetAwaiter().GetResult(); break ; } } } } }
至此直播姬的启停便实现完毕。
定时任务 定时任务的实现方式多种多样,可以利用Timer
进行简单实现,也可以使用一些定时任务的类库进行实现。
比较流行的有:其实我也就知道两个
本例程将使用Hangfire
来实现这一个功能。
安装 nuget 包 安装以下 nuget 包:
Hangfire.Core Hangfire.MemoryStorage Hangfire.Autofac Microsoft.Owin.Hosting Microsoft.Owin.Host.HttpListener Hangfire
相关内容是实现定时任务功能。
Microsoft.Owin.*
则实现了在非 IIS 进程中托管 Web 服务的功能。
插件启动时初始化 Web 服务 Hangfire 需要通过 Web 服务来展示当前的任务状态情况。
添加 IWebHost
接口,以便在插件初始化时,初始化 Web 服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 using Autofac;using System.Threading.Tasks;namespace Newbe.Mahua.Samples.LiveGirl.Services { public interface IWebHost { Task StartAsync (string baseUrl, ILifetimeScope lifetimeScope ) ; Task StopAsync () ; } }
Web 服务的初始化,需要在插件启动时进行调用。
在MahuaEvents
下添加”插件初始化事件”,并在事件内调用初始化。实现代码如下:
MahuaEvents 文件夹是本 SDK 建议将事件放置的文件夹位置。也可以不接受建议而添加在其他地方。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using Newbe.Mahua.MahuaEvents;using Newbe.Mahua.Samples.LiveGirl.Services;namespace Newbe.Mahua.Samples.LiveGirl.MahuaEvents { public class InitializationMahuaEvent : IInitializationMahuaEvent { private readonly IMahuaApi _mahuaApi; private readonly IWebHost _webHost; public InitializationMahuaEvent ( IMahuaApi mahuaApi, IWebHost webHost ) { _mahuaApi = mahuaApi; _webHost = webHost; } public void Initialized (InitializedContext context ) { _webHost.StartAsync("http://localhost:65238" , _mahuaApi.GetSourceContainer()); } } }
添加 Hangfire 初始化代码 Owin 的启动入口是一个名为Startup
的启动类,为了初始化Hangfire
,则需要创建启动类,并初始化Hangfire
。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 using Autofac;using Hangfire;using Hangfire.MemoryStorage;using Microsoft.Owin;using Owin;[assembly: OwinStartup(typeof(Newbe.Mahua.Samples.LiveGirl.Startup)) ] namespace Newbe.Mahua.Samples.LiveGirl { public class Startup { public void Configuration (IAppBuilder app, ILifetimeScope lifetimeScope ) { var config = GlobalConfiguration.Configuration; config.UseMemoryStorage(); config.UseAutofacActivator(lifetimeScope); app.UseHangfireDashboard(); app.UseHangfireServer(); } } }
实现 IWebHost 上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 using Autofac;using Microsoft.Owin.Hosting;using System;using System.Threading.Tasks;namespace Newbe.Mahua.Samples.LiveGirl.Services.Impl { public class OwinWebHost : IWebHost { private IDisposable _webhost; public Task StartAsync (string baseUrl, ILifetimeScope lifetimeScope ) { _webhost = WebApp.Start(baseUrl, app => new Startup().Configuration(app, lifetimeScope)); return Task.FromResult(0 ); } public Task StopAsync () { _webhost.Dispose(); return Task.FromResult(0 ); } } }
实现直播姬 基础设施已经在上一节完成,接下来就要实现直播姬和定时任务之间的调度代码。
获取直播间状态 直播间状态可以通过捕捉 HTTP 请求,看出如何实现。
本例程,将引入 RestSharp
nuget 包来实现 HTTP 请求。
定义直播间接口ILiveRoom
并添加实现类。
1 2 3 4 5 6 7 8 9 10 11 namespace Newbe.Mahua.Samples.LiveGirl.Services { public interface ILiveRoom { bool IsOnLive () ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 using RestSharp;namespace Newbe.Mahua.Samples.LiveGirl.Services.Impl { public class LiveRoom : ILiveRoom { public bool IsOnLive () { var client = new RestClient("https://api.live.bilibili.com" ); var req = new RestRequest("room/v1/Room/get_info?room_id=7834872&from=room" ); var resp = client.Get<Rootobject>(req); if (resp.IsSuccessful) { return resp.Data.data.live_status == 1 ; } return false ; } public class Rootobject { public int code { get ; set ; } public string msg { get ; set ; } public string message { get ; set ; } public Data data { get ; set ; } } public class Data { public int uid { get ; set ; } public string description { get ; set ; } public int live_status { get ; set ; } public int area_id { get ; set ; } public int parent_area_id { get ; set ; } public string parent_area_name { get ; set ; } public int old_area_id { get ; set ; } public string background { get ; set ; } public string title { get ; set ; } public string user_cover { get ; set ; } public string live_time { get ; set ; } public string tags { get ; set ; } public int is_anchor { get ; set ; } public string room_silent_type { get ; set ; } public int room_silent_level { get ; set ; } public int room_silent_second { get ; set ; } public string area_name { get ; set ; } public string pendants { get ; set ; } public string area_pendants { get ; set ; } public string verify { get ; set ; } } } }
Livegirl 来吧,终于可以实现直播姬了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 using Hangfire;using System.Diagnostics;using System.Threading.Tasks;namespace Newbe.Mahua.Samples.LiveGirl.Services.Impl { public class Livegirl : ILivegirl { private static readonly string JobId = "jobid" ; private readonly IMahuaApi _mahuaApi; private readonly ILiveRoom _liveRoom; public Livegirl ( IMahuaApi mahuaApi, ILiveRoom liveRoom ) { _mahuaApi = mahuaApi; _liveRoom = liveRoom; } public Task StartAsync () { RecurringJob.AddOrUpdate(JobId, () => SendMessage(), () => Cron.HourInterval(1 )); Process.Start("http://localhost:65238/hangfire/recurring" ); return Task.FromResult(0 ); } public Task StopAsnyc () { RecurringJob.RemoveIfExists(JobId); return Task.FromResult(0 ); } public void SendMessage () { if (_liveRoom.IsOnLive()) { _mahuaApi.SendGroupMessage("610394020" , "群主正在女装,前往观望?https://live.bilibili.com/7834872" ); } } } }
模块注册 以上所有的接口与实现类与接口,都不要忘记在模块中进行注册,以下是MahuaModule
的完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 using Autofac;using Newbe.Mahua.MahuaEvents;using Newbe.Mahua.Samples.LiveGirl.MahuaEvents;using Newbe.Mahua.Samples.LiveGirl.Services;using Newbe.Mahua.Samples.LiveGirl.Services.Impl;namespace Newbe.Mahua.Samples.LiveGirl { public class MahuaModule : IMahuaModule { public Module[] GetModules () { return new Module[] { new PluginModule(), new MahuaEventsModule(), new MyServiceModule(), }; } private class PluginModule : Module { protected override void Load (ContainerBuilder builder ) { base .Load(builder); builder.RegisterType<PluginInfo>() .As<IPluginInfo>(); } } private class MahuaEventsModule : Module { protected override void Load (ContainerBuilder builder ) { base .Load(builder); builder.RegisterType<InitializationMahuaEvent>() .As<IInitializationMahuaEvent>(); builder.RegisterType<PrivateMessageFromFriendReceivedMahuaEvent>() .As<IPrivateMessageFromFriendReceivedMahuaEvent>(); } } private class MyServiceModule : Module { protected override void Load (ContainerBuilder builder ) { base .Load(builder); builder.RegisterType<OwinWebHost>() .As<IWebHost>() .SingleInstance(); builder.RegisterType<Livegirl>() .As<ILivegirl>() .AsSelf(); builder.RegisterType<LiveRoom>() .As<ILiveRoom>(); } } } }
集成测试 万事具备,只欠生成。
生成解决方案,运行build.bat
,复制相关的 DLL 到对应的平台,向机器人发送消息,效果达成!
以下是 CQP 平台的测试效果。其实其他的没测试
总结 一般的定时任务只需要使用Timer
就能够实现了,引入Hangfire
主要是为了体现框架本身的可扩展性。分明是为了装逼
HTTP 的捕捉,可以使用Fiddler
等 Web 调试工具实现。又要自己学
例程中写死的字符串,应当通过文件配置进行保存,可以自行改造。
实例的项目代码,可以在源码仓库中的Newbe.Mahua.Samples
解决方案下找到。
教程链接 发布说明