关于企业微信
This commit is contained in:
@@ -118,6 +118,31 @@ namespace Ramitta.lib
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 文件解析
|
#region 文件解析
|
||||||
|
public static string? GetExeVersion(string exePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(exePath))
|
||||||
|
{
|
||||||
|
return null; // 文件不存在时返回null
|
||||||
|
}
|
||||||
|
|
||||||
|
var versionInfo = FileVersionInfo.GetVersionInfo(exePath);
|
||||||
|
string? version = versionInfo.FileVersion;
|
||||||
|
|
||||||
|
// 如果FileVersion为空,尝试使用ProductVersion
|
||||||
|
if (string.IsNullOrEmpty(version))
|
||||||
|
{
|
||||||
|
version = versionInfo.ProductVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrEmpty(version) ? null : version;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null; // 出现任何异常都返回null
|
||||||
|
}
|
||||||
|
}
|
||||||
public static bool IsPathExist(string databasePath)
|
public static bool IsPathExist(string databasePath)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(databasePath))
|
if (string.IsNullOrWhiteSpace(databasePath))
|
||||||
|
|||||||
259
Ramitta/WeComRobot.cs
Normal file
259
Ramitta/WeComRobot.cs
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user