feat(app): 初始化 OmniNotify 测试工具
创建基于 .NET 8 WPF 的桌面测试程序,支持配置通知接口、频道、标题和正文,并通过内置模板快速发送不同类型的测试消息。 实现服务检测、单次发送、自动定时发送、模板轮换、批量发送、非法频道切换和发送日志记录,便于验证 OmniNotify 本地消息 API 的基础行为与异常场景。 Initial-Commit: true
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
bin/
|
||||
obj/
|
||||
9
App.xaml
Normal file
9
App.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<Application x:Class="OmniNotifyTest.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:OmniNotifyTest"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
13
App.xaml.cs
Normal file
13
App.xaml.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Windows;
|
||||
|
||||
namespace OmniNotifyTest;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
|
||||
10
AssemblyInfo.cs
Normal file
10
AssemblyInfo.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly:ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
178
MainWindow.xaml
Normal file
178
MainWindow.xaml
Normal file
@@ -0,0 +1,178 @@
|
||||
<Window x:Class="OmniNotifyTest.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="OmniNotify 测试器"
|
||||
MinWidth="1060"
|
||||
MinHeight="720"
|
||||
Width="1180"
|
||||
Height="760"
|
||||
Background="#F4F6F8"
|
||||
FontFamily="Microsoft YaHei UI">
|
||||
<Window.Resources>
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="Height" Value="34" />
|
||||
<Setter Property="Padding" Value="14,0" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="MinHeight" Value="32" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="MinHeight" Value="32" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
<Style TargetType="GroupBox">
|
||||
<Setter Property="Margin" Value="0,0,0,12" />
|
||||
<Setter Property="Padding" Value="10" />
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="18">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<DockPanel Margin="0,0,0,14">
|
||||
<StackPanel>
|
||||
<TextBlock Text="OmniNotify 测试器" FontSize="26" FontWeight="SemiBold" Foreground="#172033" />
|
||||
<TextBlock Text="{Binding StatusText}" Margin="0,4,0,0" Foreground="#607085" />
|
||||
</StackPanel>
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Content="检测服务" Margin="0,0,8,0" Click="CheckServer_Click" />
|
||||
<Button Content="清空日志" Click="ClearLog_Click" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="330" />
|
||||
<ColumnDefinition Width="14" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border Background="White" BorderBrush="#D9E0EA" BorderThickness="1" CornerRadius="6" Padding="14">
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Top">
|
||||
<TextBlock Text="连接" FontSize="16" FontWeight="SemiBold" Foreground="#1E293B" Margin="0,0,0,10" />
|
||||
<TextBlock Text="OmniNotify 地址" Foreground="#526173" Margin="0,0,0,4" />
|
||||
<TextBox Text="{Binding Endpoint, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,12" />
|
||||
|
||||
<TextBlock Text="频道" FontSize="16" FontWeight="SemiBold" Foreground="#1E293B" Margin="0,8,0,10" />
|
||||
<ComboBox ItemsSource="{Binding Channels}"
|
||||
Text="{Binding SelectedChannel, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEditable="True"
|
||||
Margin="0,0,0,8" />
|
||||
<UniformGrid Columns="2" Margin="0,0,0,12">
|
||||
<Button Content="添加频道" Margin="0,0,4,0" Click="AddChannel_Click" />
|
||||
<Button Content="非法频道" Margin="4,0,0,0" Click="UseIllegalChannel_Click" />
|
||||
</UniformGrid>
|
||||
|
||||
<TextBlock Text="模板" FontSize="16" FontWeight="SemiBold" Foreground="#1E293B" Margin="0,8,0,10" />
|
||||
</StackPanel>
|
||||
|
||||
<ListBox ItemsSource="{Binding Presets}"
|
||||
SelectedItem="{Binding SelectedPreset}"
|
||||
DisplayMemberPath="Name"
|
||||
MinHeight="220"
|
||||
SelectionChanged="Preset_SelectionChanged" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Column="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Background="White" BorderBrush="#D9E0EA" BorderThickness="1" CornerRadius="6" Padding="16" Margin="0,0,0,14">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="18" />
|
||||
<ColumnDefinition Width="300" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock Text="消息内容" FontSize="16" FontWeight="SemiBold" Foreground="#1E293B" Margin="0,0,0,10" />
|
||||
<TextBlock Text="标题" Foreground="#526173" Margin="0,0,0,4" />
|
||||
<TextBox Text="{Binding MessageTitle, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,10" />
|
||||
<TextBlock Text="正文" Foreground="#526173" Margin="0,0,0,4" />
|
||||
<TextBox Text="{Binding Body, UpdateSourceTrigger=PropertyChanged}"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="Wrap"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
MinHeight="132"
|
||||
VerticalContentAlignment="Top" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Text="发送控制" FontSize="16" FontWeight="SemiBold" Foreground="#1E293B" Margin="0,0,0,10" />
|
||||
<Button Content="发送一次" Height="38" Click="SendOnce_Click" />
|
||||
|
||||
<GroupBox Header="自动发送" Margin="0,14,0,12">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="105" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="间隔(ms)" VerticalAlignment="Center" />
|
||||
<TextBox Grid.Column="1" Text="{Binding IntervalMilliseconds, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<TextBlock Grid.Row="1" Text="发送次数" VerticalAlignment="Center" Margin="0,8,0,0" />
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding AutoLimit, UpdateSourceTrigger=PropertyChanged}" Margin="0,8,0,0" />
|
||||
<CheckBox Grid.Row="2" Grid.ColumnSpan="2" Content="自动轮换模板" IsChecked="{Binding RotatePresets}" Margin="0,10,0,0" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<UniformGrid Columns="2">
|
||||
<Button Content="开始自动" Margin="0,0,4,0" Click="StartAuto_Click" />
|
||||
<Button Content="停止" Margin="4,0,0,0" Click="StopAuto_Click" />
|
||||
</UniformGrid>
|
||||
|
||||
<GroupBox Header="批量爆发" Margin="0,14,0,0">
|
||||
<DockPanel>
|
||||
<TextBox Text="{Binding BurstCount, UpdateSourceTrigger=PropertyChanged}" Width="80" Margin="0,0,8,0" />
|
||||
<Button Content="快速发送" Click="Burst_Click" />
|
||||
</DockPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border Grid.Row="1" Background="White" BorderBrush="#D9E0EA" BorderThickness="1" CornerRadius="6" Padding="12">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="发送日志" FontSize="16" FontWeight="SemiBold" Foreground="#1E293B" Margin="0,0,0,10" />
|
||||
<DataGrid Grid.Row="1"
|
||||
ItemsSource="{Binding Logs}"
|
||||
AutoGenerateColumns="False"
|
||||
IsReadOnly="True"
|
||||
CanUserAddRows="False"
|
||||
HeadersVisibility="Column"
|
||||
GridLinesVisibility="Horizontal">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="时间" Binding="{Binding TimeText}" Width="92" />
|
||||
<DataGridTextColumn Header="频道" Binding="{Binding Channel}" Width="120" />
|
||||
<DataGridTextColumn Header="标题" Binding="{Binding Title}" Width="180" />
|
||||
<DataGridTextColumn Header="HTTP" Binding="{Binding StatusCode}" Width="70" />
|
||||
<DataGridTextColumn Header="结果" Binding="{Binding Result}" Width="*" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
261
MainWindow.xaml.cs
Normal file
261
MainWindow.xaml.cs
Normal file
@@ -0,0 +1,261 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace OmniNotifyTest;
|
||||
|
||||
public partial class MainWindow : Window, INotifyPropertyChanged
|
||||
{
|
||||
private static readonly HttpClient Http = new() { Timeout = TimeSpan.FromSeconds(4) };
|
||||
private readonly DispatcherTimer _autoTimer = new();
|
||||
private int _autoSent;
|
||||
private int _presetIndex;
|
||||
private MessagePreset? _selectedPreset;
|
||||
private string _statusText = "准备就绪。请确认 OmniNotify 正在运行,且目标频道已在主程序中创建。";
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = this;
|
||||
|
||||
Channels.Add("default");
|
||||
Channels.Add("build");
|
||||
Channels.Add("monitor");
|
||||
Channels.Add("urgent");
|
||||
|
||||
Presets.Add(new MessagePreset("普通消息", "测试通知", "这是一条来自 OmniNotify 测试器的普通消息。"));
|
||||
Presets.Add(new MessagePreset("长正文", "长文本排版测试", "第一行:用于观察弹窗宽度、最大高度和换行。\r\n第二行:这段正文会稍微长一些,用来测试截断、跑马灯或拆分显示。\r\n第三行:消息发送时间 {time}。"));
|
||||
Presets.Add(new MessagePreset("空标题", "", "只有正文,没有标题。"));
|
||||
Presets.Add(new MessagePreset("构建成功", "Build finished", "Release 构建完成,所有测试通过。"));
|
||||
Presets.Add(new MessagePreset("告警", "CPU 使用率过高", "monitor 节点检测到 CPU 持续高于 90%,请检查后台任务。"));
|
||||
Presets.Add(new MessagePreset("特殊字符", "Symbols !@#$%", "中文、English、数字 12345,以及 JSON 转义字符:\" \\ /。"));
|
||||
|
||||
SelectedPreset = Presets[0];
|
||||
ApplyPreset(SelectedPreset);
|
||||
|
||||
_autoTimer.Tick += async (_, _) => await SendAutoTickAsync();
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public ObservableCollection<string> Channels { get; } = [];
|
||||
public ObservableCollection<MessagePreset> Presets { get; } = [];
|
||||
public ObservableCollection<SendLogItem> Logs { get; } = [];
|
||||
|
||||
public string Endpoint { get; set; } = "http://127.0.0.1:19845/notify";
|
||||
public string SelectedChannel { get; set; } = "default";
|
||||
public string MessageTitle { get; set; } = "";
|
||||
public string Body { get; set; } = "";
|
||||
public int IntervalMilliseconds { get; set; } = 1000;
|
||||
public int AutoLimit { get; set; } = 20;
|
||||
public int BurstCount { get; set; } = 10;
|
||||
public bool RotatePresets { get; set; } = true;
|
||||
|
||||
public string StatusText
|
||||
{
|
||||
get => _statusText;
|
||||
set => SetField(ref _statusText, value);
|
||||
}
|
||||
|
||||
public MessagePreset? SelectedPreset
|
||||
{
|
||||
get => _selectedPreset;
|
||||
set => SetField(ref _selectedPreset, value);
|
||||
}
|
||||
|
||||
private async void CheckServer_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var baseUri = new Uri(Endpoint);
|
||||
var root = new Uri(baseUri.GetLeftPart(UriPartial.Authority));
|
||||
var response = await Http.GetAsync(root);
|
||||
StatusText = response.IsSuccessStatusCode
|
||||
? $"服务可达:{root}"
|
||||
: $"服务响应异常:HTTP {(int)response.StatusCode}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusText = $"服务不可达:{ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private async void SendOnce_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await SendCurrentAsync("手动");
|
||||
}
|
||||
|
||||
private void StartAuto_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_autoSent = 0;
|
||||
_autoTimer.Interval = TimeSpan.FromMilliseconds(Math.Clamp(IntervalMilliseconds, 100, 60000));
|
||||
_autoTimer.Start();
|
||||
StatusText = $"自动发送已开始:间隔 {_autoTimer.Interval.TotalMilliseconds:0} ms。";
|
||||
}
|
||||
|
||||
private void StopAuto_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
StopAuto("自动发送已停止。");
|
||||
}
|
||||
|
||||
private async void Burst_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var count = Math.Clamp(BurstCount, 1, 200);
|
||||
StatusText = $"开始批量发送 {count} 条消息。";
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (RotatePresets)
|
||||
{
|
||||
ApplyNextPreset();
|
||||
}
|
||||
|
||||
await SendCurrentAsync("批量", i + 1);
|
||||
}
|
||||
|
||||
StatusText = $"批量发送完成:{count} 条。";
|
||||
}
|
||||
|
||||
private void AddChannel_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var channel = string.IsNullOrWhiteSpace(SelectedChannel) ? "default" : SelectedChannel.Trim();
|
||||
if (!Channels.Contains(channel))
|
||||
{
|
||||
Channels.Add(channel);
|
||||
}
|
||||
|
||||
SelectedChannel = channel;
|
||||
OnPropertyChanged(nameof(SelectedChannel));
|
||||
}
|
||||
|
||||
private void UseIllegalChannel_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SelectedChannel = $"illegal-{DateTime.Now:HHmmss}";
|
||||
OnPropertyChanged(nameof(SelectedChannel));
|
||||
StatusText = "已切换到一个通常不存在的频道,可用于测试 IllegalChannel。";
|
||||
}
|
||||
|
||||
private void ClearLog_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Logs.Clear();
|
||||
StatusText = "日志已清空。";
|
||||
}
|
||||
|
||||
private void Preset_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||
{
|
||||
if (SelectedPreset is not null)
|
||||
{
|
||||
ApplyPreset(SelectedPreset);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendAutoTickAsync()
|
||||
{
|
||||
if (AutoLimit > 0 && _autoSent >= AutoLimit)
|
||||
{
|
||||
StopAuto($"自动发送完成:{_autoSent} 条。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (RotatePresets)
|
||||
{
|
||||
ApplyNextPreset();
|
||||
}
|
||||
|
||||
_autoSent++;
|
||||
await SendCurrentAsync("自动", _autoSent);
|
||||
}
|
||||
|
||||
private async Task SendCurrentAsync(string mode, int? sequence = null)
|
||||
{
|
||||
var channel = string.IsNullOrWhiteSpace(SelectedChannel) ? "default" : SelectedChannel.Trim();
|
||||
var message = new NotifyMessage(
|
||||
channel,
|
||||
ExpandTokens(MessageTitle, sequence),
|
||||
ExpandTokens(Body, sequence));
|
||||
|
||||
try
|
||||
{
|
||||
var response = await Http.PostAsJsonAsync(Endpoint, message);
|
||||
var result = await response.Content.ReadAsStringAsync();
|
||||
AddLog(channel, message.Title, (int)response.StatusCode, string.IsNullOrWhiteSpace(result) ? response.ReasonPhrase ?? mode : result);
|
||||
StatusText = $"{mode}发送完成:HTTP {(int)response.StatusCode} {response.ReasonPhrase}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddLog(channel, message.Title, 0, ex.Message);
|
||||
StatusText = $"{mode}发送失败:{ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyPreset(MessagePreset preset)
|
||||
{
|
||||
MessageTitle = preset.Title;
|
||||
Body = preset.Body;
|
||||
OnPropertyChanged(nameof(MessageTitle));
|
||||
OnPropertyChanged(nameof(Body));
|
||||
}
|
||||
|
||||
private void ApplyNextPreset()
|
||||
{
|
||||
if (Presets.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_presetIndex = (_presetIndex + 1) % Presets.Count;
|
||||
SelectedPreset = Presets[_presetIndex];
|
||||
ApplyPreset(SelectedPreset);
|
||||
}
|
||||
|
||||
private string ExpandTokens(string value, int? sequence)
|
||||
{
|
||||
return value
|
||||
.Replace("{time}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))
|
||||
.Replace("{seq}", (sequence ?? Logs.Count + 1).ToString());
|
||||
}
|
||||
|
||||
private void StopAuto(string message)
|
||||
{
|
||||
_autoTimer.Stop();
|
||||
StatusText = message;
|
||||
}
|
||||
|
||||
private void AddLog(string channel, string title, int statusCode, string result)
|
||||
{
|
||||
Logs.Insert(0, new SendLogItem(DateTime.Now, channel, title, statusCode == 0 ? "-" : statusCode.ToString(), result));
|
||||
while (Logs.Count > 500)
|
||||
{
|
||||
Logs.RemoveAt(Logs.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
private void SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record MessagePreset(string Name, string Title, string Body);
|
||||
|
||||
public sealed record NotifyMessage(string Channel, string Title, string Body);
|
||||
|
||||
public sealed record SendLogItem(DateTime Time, string Channel, string Title, string StatusCode, string Result)
|
||||
{
|
||||
public string TimeText => Time.ToString("HH:mm:ss");
|
||||
}
|
||||
11
OmniNotifyTest.csproj
Normal file
11
OmniNotifyTest.csproj
Normal file
@@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
37
README.md
Normal file
37
README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# OmniNotifyTest
|
||||
|
||||
OmniNotifyTest 是一个用于测试 OmniNotify 本地消息 API 的 WPF 桌面工具。
|
||||
|
||||
## 功能
|
||||
|
||||
- 手动向 OmniNotify 发送单条通知
|
||||
- 编辑目标地址、频道、标题和正文
|
||||
- 使用内置模板测试普通消息、长文本、空标题、特殊字符和告警内容
|
||||
- 自动定时发送,可选择轮换模板
|
||||
- 快速批量发送,用于测试限流和熔断
|
||||
- 一键切换到通常不存在的频道,用于测试 `IllegalChannel`
|
||||
- 记录每次请求的时间、频道、标题、HTTP 状态和响应结果
|
||||
|
||||
## 运行
|
||||
|
||||
先启动 OmniNotify,并确保需要测试的频道已经在 OmniNotify 主程序中创建。
|
||||
|
||||
```powershell
|
||||
dotnet run
|
||||
```
|
||||
|
||||
默认发送地址为:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:19845/notify
|
||||
```
|
||||
|
||||
OmniNotify 接收的 JSON 结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"channel": "default",
|
||||
"title": "测试通知",
|
||||
"body": "这是一条测试消息。"
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user