259 lines
8.9 KiB
C#
259 lines
8.9 KiB
C#
using HelixToolkit.Maths;
|
||
using NPOI.HPSF;
|
||
using System;
|
||
using System.IO;
|
||
using System.Net;
|
||
using System.Net.Http;
|
||
using System.Text;
|
||
using System.Text.Json;
|
||
using System.Threading.Tasks;
|
||
namespace Ramitta
|
||
{
|
||
public class WeComRobot
|
||
{
|
||
private readonly string _webhookUrl;
|
||
private readonly HttpClient _httpClient;
|
||
|
||
public WeComRobot(string webhookUrl)
|
||
{
|
||
_webhookUrl = webhookUrl;
|
||
_httpClient = new HttpClient();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送文本消息
|
||
/// </summary>
|
||
public async Task<bool> SendTextMessageAsync(string content, string[] mentionedList = null, string[] mentionedMobileList = null)
|
||
{
|
||
var message = new
|
||
{
|
||
msgtype = "text",
|
||
text = new
|
||
{
|
||
content = content,
|
||
mentioned_list = mentionedList,
|
||
mentioned_mobile_list = mentionedMobileList
|
||
}
|
||
};
|
||
|
||
return await SendMessageAsync(message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送Markdown消息
|
||
/// </summary>
|
||
public async Task<bool> SendMarkdownMessageAsync(string content)
|
||
{
|
||
var message = new
|
||
{
|
||
msgtype = "markdown",
|
||
markdown = new
|
||
{
|
||
content = content
|
||
}
|
||
};
|
||
|
||
return await SendMessageAsync(message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送图片消息
|
||
/// </summary>
|
||
public async Task<bool> SendImageMessageAsync(string base64, string md5)
|
||
{
|
||
var message = new
|
||
{
|
||
msgtype = "image",
|
||
image = new
|
||
{
|
||
base64 = base64,
|
||
md5 = md5
|
||
}
|
||
};
|
||
|
||
return await SendMessageAsync(message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送图文消息
|
||
/// </summary>
|
||
public async Task<bool> SendNewsMessageAsync(Article[] articles)
|
||
{
|
||
var message = new
|
||
{
|
||
msgtype = "news",
|
||
news = new
|
||
{
|
||
articles = articles
|
||
}
|
||
};
|
||
|
||
return await SendMessageAsync(message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送文件消息
|
||
/// </summary>
|
||
public async Task<bool> SendFileMessageAsync(string mediaId)
|
||
{
|
||
var message = new
|
||
{
|
||
msgtype = "file",
|
||
file = new
|
||
{
|
||
media_id = mediaId
|
||
}
|
||
};
|
||
|
||
return await SendMessageAsync(message);
|
||
}
|
||
|
||
private async Task<bool> SendMessageAsync(object message)
|
||
{
|
||
try
|
||
{
|
||
var json = JsonSerializer.Serialize(message);
|
||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||
|
||
var response = await _httpClient.PostAsync(_webhookUrl, content);
|
||
var responseContent = await response.Content.ReadAsStringAsync();
|
||
|
||
if (response.IsSuccessStatusCode)
|
||
{
|
||
var result = JsonSerializer.Deserialize<WeComResponse>(responseContent);
|
||
return result?.errcode == 0;
|
||
}
|
||
|
||
Console.WriteLine($"发送失败: {responseContent}");
|
||
return false;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"发送消息异常: {ex.Message}");
|
||
return false;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 上传文件到企业微信并获取media_id (修正版)
|
||
/// </summary>
|
||
public async Task<string> UploadFileAsync(string filePath,string? fileName=null)
|
||
{
|
||
try
|
||
{
|
||
// 1. 从webhook地址中提取key
|
||
var uri = new Uri(_webhookUrl);
|
||
var key = System.Web.HttpUtility.ParseQueryString(uri.Query)["key"];
|
||
string uploadUrl = $"https://qyapi.weixin.qq.com/cgi-bin/webhook/upload_media?key={key}&type=file";
|
||
|
||
// 2. 准备文件和边界
|
||
if(fileName==null) fileName = Path.GetFileName(filePath);
|
||
var fileBytes = await File.ReadAllBytesAsync(filePath);
|
||
string boundary = "------------------------" + DateTime.Now.Ticks.ToString("x"); // 随机边界字符串
|
||
string startBoundary = "--" + boundary;
|
||
string endBoundary = "--" + boundary + "--";
|
||
|
||
// 3. 创建并配置HttpWebRequest
|
||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uploadUrl);
|
||
request.Method = "POST";
|
||
request.ContentType = "multipart/form-data; boundary=" + boundary;
|
||
request.Timeout = 30000; // 可根据需要调整超时
|
||
|
||
// 4. 手动构建并写入请求体(关键步骤)
|
||
using (Stream requestStream = await request.GetRequestStreamAsync())
|
||
{
|
||
using (var writer = new StreamWriter(requestStream, Encoding.UTF8, 1024, true))
|
||
{
|
||
// 4.1 写入开始边界 (必须有回车换行)
|
||
await writer.WriteAsync(startBoundary + "\r\n");
|
||
await writer.FlushAsync();
|
||
|
||
// 4.2 写入文件头信息 (注意格式中的双引号)
|
||
string header = $"Content-Disposition: form-data; name=\"media\"; filename=\"{fileName}\"\r\n";
|
||
header += "Content-Type: application/octet-stream\r\n";
|
||
header += "\r\n"; // 文件头结束后需要一个额外的空行(即两个连续的\r\n)
|
||
await writer.WriteAsync(header);
|
||
await writer.FlushAsync();
|
||
|
||
// 4.3 直接写入文件字节
|
||
await requestStream.WriteAsync(fileBytes, 0, fileBytes.Length);
|
||
await writer.WriteAsync("\r\n"); // 文件内容后需要一个回车换行
|
||
await writer.FlushAsync();
|
||
|
||
// 4.4 写入结束边界
|
||
await writer.WriteAsync(endBoundary + "\r\n");
|
||
await writer.FlushAsync();
|
||
}
|
||
}
|
||
|
||
// 5. 获取并解析响应
|
||
using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
|
||
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
|
||
{
|
||
string responseContent = await reader.ReadToEndAsync();
|
||
Console.WriteLine($"上传响应: {responseContent}");
|
||
|
||
using (var doc = JsonDocument.Parse(responseContent))
|
||
{
|
||
if (doc.RootElement.TryGetProperty("media_id", out var mediaIdElement))
|
||
{
|
||
return mediaIdElement.GetString();
|
||
}
|
||
else
|
||
{
|
||
// 解析错误信息
|
||
var errcode = doc.RootElement.GetProperty("errcode").GetInt32();
|
||
var errmsg = doc.RootElement.GetProperty("errmsg").GetString();
|
||
Console.WriteLine($"文件上传失败: errcode={errcode}, errmsg={errmsg}");
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (WebException ex)
|
||
{
|
||
// 尝试读取错误响应体
|
||
if (ex.Response is HttpWebResponse errorResponse)
|
||
{
|
||
using (var reader = new StreamReader(errorResponse.GetResponseStream()))
|
||
{
|
||
string errorContent = await reader.ReadToEndAsync();
|
||
Console.WriteLine($"HTTP请求失败 ({errorResponse.StatusCode}): {errorContent}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine($"网络请求异常: {ex.Message}");
|
||
}
|
||
return null;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"文件上传过程异常: {ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 图文消息文章
|
||
/// </summary>
|
||
public class Article
|
||
{
|
||
public string title { get; set; }
|
||
public string description { get; set; }
|
||
public string url { get; set; }
|
||
public string picurl { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 企业微信响应
|
||
/// </summary>
|
||
public class WeComResponse
|
||
{
|
||
public int errcode { get; set; }
|
||
public string errmsg { get; set; }
|
||
}
|
||
|
||
} |