[水]起于2026-01-31 23:03的一件事

Austerity(Misaka10548) Lv1

起于

事情起于这样我们群中一句话:

为了不借助DDNS就搞到电脑的IP,我打算搞个自动的Python脚本获取IP并邮件发送
这样子春节就可以远程操控

霂里梦呓

(他)打算如下

  1. 自启动
  2. 检测网络通断
  3. ipconfig获取ip
  4. 发邮件给自己

虽然还没有开始怎么学习编程,我打算拿来练练手,只不过是用C#写

正文

构思

基本实现

  • 自启动:计划任务即可
  • 检测网络通断:用API实现,不采用当时一开始的ping的想法
  • 获取IP:API
  • 发邮件SMTP MS Graph API

没有什么复杂的地方,也就是一个顺序过程

升级

自启动后循环执行过程,不再通过计划任务多次调用

如果网络不可用则5分钟检查一次,直到成功(或者使用库中相关方法?如果存在)。

查资料

有关基本实现

网络部分

很容易搜索到这个文档: 网络可用性 - .NET | Microsoft Learn

里面提到了 System.Net.NetworkInformation 命名空间,接着往下看文档

找到NetworkInterfaceNetworkInterface.GetIsNetworkAvailable 方法可以获取网络可用性

其中还有GetAllNetworkInterfaces()方法,返回NetworkInterface[]对象数组

根据查询途中遇到的文档示例:NetworkInterface[] -> NetworkInterface -> IPInterfaceProperties

要获取网络配置,应该是先获取网络接口对象,使用GetIPProperties()方法,返回IPInterfaceProperties对象

查看IPInterfaceProperties 类文档

“提供有关支持 Internet 协议版本 4 (IPv4) 或 Internet 协议版本 6 (IPv6) 的网络接口的信息。” 很接近我们想要的获取IP了

示例基本上达成了我们的目的,只需要稍加改动,结合NetworkInterface.GetIPProperties 方法下的示例

邮件部分

找到SmtpClient 类MailMessage 类

不过,在SmtpClient类中,文档明确提到“不建议使用SmtpClient 类进行新的开发,因为 SmtpClient 不支持许多新式协议。 请改用 MailKit 或其他库。”,我先不管他,仍然使用

参考示例修改即可

我打算使用Microsoft的邮箱发送,结果看了如何设置多功能设备或应用程序以使用 Microsoft 365 或 Office 365 发送电子邮件才发现,这样子发邮件需要账户密码才行,我不太想这样。

只能改用MS Graph API了,记得里面有Email.Send权限什么的,应该可以。

看文档根据方案选择Microsoft Graph 身份验证提供程序看得一头雾水。还是问AI吧。

💎 总结:如何选?

  1. 你的程序是无后台、由用户触发、代表个人发信吗?授权码流(方案二)
  2. 你的程序是无人值守服务、定时任务、或需要发信给大量用户吗?客户端凭据流 + 访问策略锁定邮箱(方案一)
  3. 你是 SharePoint 扩展或特定微软低代码平台? → 实质也是授权码流,但由平台托管登录。

如果你的场景是典型的 ASP.NET Core 后台服务,直接套用客户端凭据流 + 访问策略是最干净、最合规的做法。需要我提供该模式下配合应用访问策略锁定特定发信邮箱的 PowerShell 脚本示例吗?

Deepseek-V3.2

我只截了一部分,看AI的话,我应该选择方案一,就这样。

根据方案选择Microsoft Graph 身份验证提供程序中很容易找到客户端凭据提供程序节。

邮件发送有user:sendMail

参考文档即可。

后续更改

构想

基本实现构想

graph TD
    A[从计划任务启动] --> B1{判断网络连通性}
    subgraph 网络
        B1 -->|未连接到互联网| C1[等待计划任务再次调用(退出,1)]
        B1 -->|连接到互联网| D1[获取网络接口]
        D1 -->|遍历接口| E1[获取网络接口属性]
        E1 --> F1[获取地址]
    end

    F1 --> C[组织邮件内容]
    C --> C2

    subgraph 邮件
        C2[填入内容]
        B2[初始化graphClient] --> C2["初始化信息邮件(收件人等)"]
        C2 --> D2["发送(随后退出,0)"]
    end

写代码&配置

程序

添加Azure.Identity 包Microsoft.Graph 包

这里采用了四个示例,分别复制自GetIPProperties 方法IPInterfaceProperties 类客户端凭据提供程序使用 JSON 格式发送新电子邮件

Rider帮我在组织邮件内容时提示了Environment.NewLine挺好的,原来还有这个东西,学习了。

不过据AI(DS)所说 SMTP 协议标准(以及 Microsoft Graph 邮件 API)通常要求换行符为 \r\n

根据AI询问,我选择这里使用StringBuilder来组织字符串内容,然后Append中手动使用 \r\n

最后代码如下:

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
using System.Net.NetworkInformation;
using System.Text;
using Azure.Identity;
using Microsoft.Graph;
using Microsoft.Graph.Models;
using Microsoft.Graph.Users.Item.SendMail;

static string GetEmailContent()
{
var sb = new StringBuilder();

// 获取全部的网络接口,进行遍历
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in nics)
{
// 滤除既不支持v4也不支持v6的接口
if (!(adapter.Supports(NetworkInterfaceComponent.IPv4) || adapter.Supports(NetworkInterfaceComponent.IPv6)))
{
continue;
}

// 添加接口信息
sb.Append(adapter.Description + "\r\n");
sb.Append(String.Empty.PadLeft(adapter.Description.Length,'=') + "\r\n");

// 获得接口属性信息,遍历添加多播和单播IP地址
IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
MulticastIPAddressInformationCollection multiCast = adapterProperties.MulticastAddresses;
if (multiCast != null)
{
foreach (MulticastIPAddressInformation multi in multiCast)
{
sb.Append($" Multicast Address ....................... : {multi.Address} " + "\r\n");
}
sb.Append("\r\n");
}
UnicastIPAddressInformationCollection uniCast = adapterProperties.UnicastAddresses;
if (uniCast != null)
{
foreach (UnicastIPAddressInformation uni in uniCast)
{
sb.Append($" Unicast Address ....................... : {uni.Address} " + "\r\n");
}
sb.Append("\r\n");
}
}

// StringBuilder不是String
return sb.ToString();
}

async Task SendEmailByGraphApi(string content)
{
// 一些Graph客户端的基本设置
var scopes = new[] { "https://graph.microsoft.com/.default" };

var clientId = "YOUR_CLIENT_ID";
var tenantId = "YOUR_TENANT_ID";
var clientSecret = "YOUR_CLIENT_SECRET";

var options = new ClientSecretCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
};

var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret, options);

var graphClient = new GraphServiceClient(clientSecretCredential, scopes);

// 组织邮件
var requestBody = new SendMailPostRequestBody
{
Message = new Message
{
Subject = "IP",
Body = new ItemBody
{
ContentType = BodyType.Text,
Content = content,
},
ToRecipients = new List<Recipient>
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = "frannis@contoso.com",
},
},
},
},
SaveToSentItems = false,
};

// 非登录应用程序调用不能用Me方法,在示例踩坑了
await graphClient.Users["sender@contoso.com"].SendMail.PostAsync(requestBody);
}

if (!NetworkInterface.GetIsNetworkAvailable())
return 1;

string content = GetEmailContent();
await SendEmailByGraphApi(content);
return 0;

Graph应用程序

登录Azure Portal,找到应用注册,注册EmailSender应用程序

配置中找到API 权限。 添加权限->Microsoft Graph->应用程序权限->Mail.Send->添加权限

我自己用的,这里就直接代表授予管理员同意

然后在应用程序概述中,复制clientIdtenantId(应用程序(客户端) ID 和 目录(租户) ID),写到YOUR_CLIENT_IDYOUR_TENANT_ID

然后转到证书和密码,选择新客户端密码,添加一个,然后复制值,写到YOUR_CLIENT_SECRET

信息

记得妥善保管,因为值内容仅在初次创建时显示。

基本功能完成

有关后续的改进,等我想写了再写吧;感谢Rider的支持

其他

主要参考了微软的文档(主要就是那些示例),并有的时候询问了AI(Deepseek)。

主要问了:

  • 网络连通性判断
  • Microsoft365 使用 Graph API 发送邮件可以使用哪些身份认证方式
  • 一些错误的修复
  • 标题: [水]起于2026-01-31 23:03的一件事
  • 作者: Austerity(Misaka10548)
  • 创建于 : 2026-02-11 18:44:13
  • 更新于 : 2026-02-12 11:01:42
  • 链接: https://austerity.irislc.net/2026/02/11/7875c8a8/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论