Compare commits

..

No commits in common. "master" and "V2.0" have entirely different histories.
master ... V2.0

17 changed files with 84 additions and 179 deletions

View File

@ -14,6 +14,10 @@ jobs:
name: Check out the repo name: Check out the repo
uses: actions/checkout@v2 uses: actions/checkout@v2
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Cache node modules NPM - name: Cache node modules NPM
uses: actions/cache@v2 uses: actions/cache@v2
env: env:

View File

@ -1,59 +0,0 @@
name: docker_dev
on:
push:
branches: [ dev ]
pull_request:
branches: [ dev ]
jobs:
build:
runs-on: ubuntu-latest
steps:
-
name: Check out the repo
uses: actions/checkout@v2
- name: Cache node modules NPM
uses: actions/cache@v2
env:
cache-name: cache-node-modules-NPM
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./Inotify.Vue/package.json') }}
restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./Inotify.Vue/package.json') }}
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ./Inotify.Vue/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./Inotify.Vue/package.json') }}
restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./Inotify.Vue/package.json') }}
- name: InstallNode and BuildVue
run: |
cd ./Inotify.Vue
npm install
npm run build:prod
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Docker Build & Push to Docker Hub For Service
uses: docker/build-push-action@v2
with:
context: .
file: ./Inotify/Dockerfile
platforms: linux/amd64
push: true
tags: ${{ secrets.DOCKERHUB_TAG }}:dev
- name: 'Report Suecss'
run: curl ${{ secrets.INOTIFY }}/Inotify/DevIsOk!

View File

@ -12,6 +12,10 @@ jobs:
name: Check out the repo name: Check out the repo
uses: actions/checkout@v2 uses: actions/checkout@v2
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Cache node modules NPM - name: Cache node modules NPM
uses: actions/cache@v2 uses: actions/cache@v2
env: env:

View File

@ -17,6 +17,7 @@
"axios": "0.18.1", "axios": "0.18.1",
"core-js": "3.6.5", "core-js": "3.6.5",
"echarts": "^4.9.0", "echarts": "^4.9.0",
"element-plus": "^1.0.2-beta.32",
"element-ui": "2.13.2", "element-ui": "2.13.2",
"js-cookie": "2.2.0", "js-cookie": "2.2.0",
"moment": "^2.29.1", "moment": "^2.29.1",

View File

@ -49,9 +49,10 @@ namespace Inotify.Controllers
sendTemplate.IsActive = sendAuthInfo.Active; sendTemplate.IsActive = sendAuthInfo.Active;
sendTemplate.AuthToTemplate(sendAuthInfo.AuthData); sendTemplate.AuthToTemplate(sendAuthInfo.AuthData);
userSendTemplates.Add(sendTemplate); userSendTemplates.Add(sendTemplate);
}
var bark = sendTemplate.Values.FirstOrDefault(e => e.Name == nameof(BarkSendTemplate.Auth.SendUrl)); if (barkTemplateAttribute.Key == sendTemplate.Type)
if(bark!=null) bark .Value = ""; {
sendTemplate.Values.FirstOrDefault(e => e.Name == nameof(BarkSendTemplate.Auth.SendUrl)).Value = "";
} }
} }

View File

@ -120,7 +120,7 @@ namespace Inotify.Controllers
public IActionResult GetSendInfos(string? start, string? end) public IActionResult GetSendInfos(string? start, string? end)
{ {
var templates = SendTaskManager.Instance.GetInputTemeplates(); var templates = SendTaskManager.Instance.GetInputTemeplates();
var sendInfos = DBManager.Instance.DBase.Fetch<SendInfo>().Where(e=>!string.IsNullOrEmpty( e.TemplateID)).ToList(); var sendInfos = DBManager.Instance.DBase.Fetch<SendInfo>();
var sendInfoQuerys = sendInfos.Where(e => int.Parse(e.Date) >= int.Parse(start) && int.Parse(e.Date) <= int.Parse(end)).ToList(); var sendInfoQuerys = sendInfos.Where(e => int.Parse(e.Date) >= int.Parse(start) && int.Parse(e.Date) <= int.Parse(end)).ToList();
var sendInfoGroups = sendInfoQuerys.GroupBy(e => e.Date).Select(e => new { date = e.Key, count = e.Sum(item => item.Count) }).ToList(); var sendInfoGroups = sendInfoQuerys.GroupBy(e => e.Date).Select(e => new { date = e.Key, count = e.Sum(item => item.Count) }).ToList();
var sendTypeInfoGroups = sendInfoQuerys.GroupBy(e => e.TemplateID).Select(e => new { date = e.Key, count = e.Sum(item => item.Count) }).ToList(); var sendTypeInfoGroups = sendInfoQuerys.GroupBy(e => e.TemplateID).Select(e => new { date = e.Key, count = e.Sum(item => item.Count) }).ToList();

View File

@ -164,10 +164,17 @@ namespace Inotify.Data
return DBase.Query<SendUserInfo>().FirstOrDefault(e => e.UserName == userName); return DBase.Query<SendUserInfo>().FirstOrDefault(e => e.UserName == userName);
} }
public string GetSendAuthInfo(string key, out string guid) public string GetSendAuthInfo(string token, out string guid)
{ {
guid = null; guid = string.Empty;
var authInfo = DBManager.Instance.DBase.Query<SendAuthInfo>().FirstOrDefault(e => e.Key== key.ToUpper()); var upToekn = token.ToUpper();
var userInfo = DBManager.Instance.DBase.Query<SendUserInfo>().FirstOrDefault(e => e.Token == upToekn && e.Active);
if (userInfo == null)
{
return null;
}
var authInfo = DBManager.Instance.DBase.Query<SendAuthInfo>().FirstOrDefault(e => e.Id == userInfo.SendAuthId && e.UserId == userInfo.Id);
if (authInfo == null) if (authInfo == null)
{ {
return null; return null;
@ -200,13 +207,12 @@ namespace Inotify.Data
{ {
var codeVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; var codeVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
var migrationBuilder = new MigrationBuilder(MigrationName, DBase);
var versionProvider = new DatabaseCurrentVersionProvider(DBase); var versionProvider = new DatabaseCurrentVersionProvider(DBase);
if (!m_migrator.TableExists<SystemInfo>()) if (!m_migrator.TableExists<SystemInfo>())
{ {
var migrationBuilder = new MigrationBuilder(MigrationName, DBase);
migrationBuilder.Append(new Version(codeVersion.ToString()), new LatestMigration()); migrationBuilder.Append(new Version(codeVersion.ToString()), new LatestMigration());
migrationBuilder.Execute();
versionProvider.SetMigrationVersion(MigrationName, new Version(codeVersion.ToString())); versionProvider.SetMigrationVersion(MigrationName, new Version(codeVersion.ToString()));
} }
else else

View File

@ -25,21 +25,6 @@ namespace Inotify.Data
key = "administrators", key = "administrators",
Value = "admin" Value = "admin"
}); });
Migrator.Database.Insert(new SystemInfo()
{
key = "barkKeyId",
Value = "TEg0VDlWNVU0Ug==".Base64Decode(),
});
Migrator.Database.Insert(new SystemInfo()
{
key = "barkTeamId",
Value = "NVU4TEJSWEczQQ==".Base64Decode(),
});
Migrator.Database.Insert(new SystemInfo()
{
key = "barkPrivateKey",
Value = "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR1RBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJIa3dkd0lCQVFRZzR2dEMzZzVMNUhnS0dKMitUMWVBMHRPaXZSRXZFQVkyZytqdVJYSmtZTDJnQ2dZSUtvWkl6ajBEQVFlaFJBTkNBQVNtT3MzSmtTeW9HRVdac1VHeEZzLzRwdzFySWxTVjJJQzE5TTh1M0c1a3EzNnVwT3d5RldqOUdpM0VqYzlkM3NDNytTSFJxWHJFQUpvdzgvN3RScFYrCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=".Base64Decode()
});
} }
if (!Migrator.TableExists<SendInfo>()) if (!Migrator.TableExists<SendInfo>())

View File

@ -6,8 +6,8 @@
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<StartupObject>Inotify.Program</StartupObject> <StartupObject>Inotify.Program</StartupObject>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AssemblyVersion>2.3.0.0</AssemblyVersion> <AssemblyVersion>2.0.0.4</AssemblyVersion>
<FileVersion>2.3.0.0</FileVersion> <FileVersion>2.0.0.4</FileVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@ -18,7 +18,8 @@
<PackageReference Include="FluentEmail.Core" Version="3.0.0" /> <PackageReference Include="FluentEmail.Core" Version="3.0.0" />
<PackageReference Include="FluentEmail.Liquid" Version="3.0.0" /> <PackageReference Include="FluentEmail.Liquid" Version="3.0.0" />
<PackageReference Include="FluentEmail.Smtp" Version="3.0.0" /> <PackageReference Include="FluentEmail.Smtp" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.18" /> <PackageReference Include="jose-jwt" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.12" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.3" /> <PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.3" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.9" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.10.9" />
@ -27,7 +28,7 @@
<PackageReference Include="NPoco.Migrations" Version="0.3.2" /> <PackageReference Include="NPoco.Migrations" Version="0.3.2" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
<PackageReference Include="System.Runtime.Caching" Version="5.0.0" /> <PackageReference Include="System.Runtime.Caching" Version="5.0.0" />
<PackageReference Include="Telegram.Bot" Version="18.0.0" /> <PackageReference Include="Telegram.Bot" Version="15.7.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,7 +1,13 @@
using CorePush.Apple; using CorePush.Apple;
using Jose;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
namespace Inotify.Sends.Products namespace Inotify.Sends.Products
{ {
@ -26,50 +32,52 @@ namespace Inotify.Sends.Products
[InputTypeAttribte(6, "SendUrl", "SendUrl", "SendUrl", true, true)] [InputTypeAttribte(6, "SendUrl", "SendUrl", "SendUrl", true, true)]
public string SendUrl { get; set; } public string SendUrl { get; set; }
} }
[SendMethodKey("3B6DE04D-A9EF-4C91-A151-60B7425C5AB2", "Bark", Order = 2999, Waring = "BARK通道勿手动添加请使用APP添加BARK地址绑定")] [SendMethodKey("3B6DE04D-A9EF-4C91-A151-60B7425C5AB2", "Bark", Order = 2999, Waring = "BARK通道勿手动添加请使用APP添加BARK地址绑定")]
public class BarkSendTemplate : SendTemplate<BarkAuth> public class BarkSendTemplate : SendTemplate<BarkAuth>
{ {
private static string KeyID;
private static string TeamID;
private static ApnSender apnSender; private static ApnSender apnSender;
public override bool SendMessage(SendMessage message) public override bool SendMessage(SendMessage message)
{ {
if (apnSender == null) if (apnSender == null)
{ {
var keyID = SendCacheStore.GetSystemValue("barkKeyId"); KeyID = SendCacheStore.GetSystemValue("barkKeyId");
var teamID = SendCacheStore.GetSystemValue("barkTeamId"); TeamID = SendCacheStore.GetSystemValue("barkTeamId");
var privateKey = SendCacheStore.GetSystemValue("barkPrivateKey"); var privateKey = SendCacheStore.GetSystemValue("barkPrivateKey");
var privateKeyContent = privateKey.Split('\n')[1]; var privateKeyContent = privateKey.Split('\n')[1];
var decodeKey = Convert.FromBase64String(privateKeyContent);
var apnSettings = new ApnSettings() var apnSettings = new ApnSettings()
{ {
TeamId = teamID, TeamId = TeamID,
AppBundleIdentifier = "me.fin.bark", AppBundleIdentifier = "me.fin.bark",
P8PrivateKey = privateKeyContent, P8PrivateKey = privateKeyContent,
ServerType = ApnServerType.Production, ServerType = ApnServerType.Production,
P8PrivateKeyId = keyID, P8PrivateKeyId = KeyID,
}; };
apnSender = new ApnSender(apnSettings, new HttpClient()); apnSender = new ApnSender(apnSettings, new HttpClient());
} }
var payload = new AppleNotification( var payload = new AppleNotification(
Guid.NewGuid(), Guid.NewGuid(),
message.Data, message.Data,
message.Title) message.Title);
{
IsArchive = Auth.IsArchive,
AutoMaticallyCopy=Auth.AutoMaticallyCopy,
};
payload.Aps.Sound = Auth.Sound;
var response = apnSender.Send(payload, Auth.DeviceToken); var response = apnSender.Send(payload, Auth.DeviceToken);
if (response.IsSuccess) if (response.IsSuccess)
return true; return true;
return false; return false;
} }
} }
public class AppleNotification public class AppleNotification
@ -83,12 +91,8 @@ namespace Inotify.Sends.Products
[JsonProperty("body")] [JsonProperty("body")]
public string Body { get; set; } public string Body { get; set; }
} }
[JsonProperty("sound")]
public string Sound { get; set; }
[JsonProperty("alert")] [JsonProperty("alert")]
public Alert AlertBody { get; set; } public Alert AlertBody { get; set; }
@ -115,11 +119,5 @@ namespace Inotify.Sends.Products
[JsonProperty("id")] [JsonProperty("id")]
public Guid Id { get; set; } public Guid Id { get; set; }
[JsonProperty("isarchive")]
public string IsArchive { get; set; }
[JsonProperty("automaticallycopy")]
public string AutoMaticallyCopy { get; set; }
} }
} }

View File

@ -1,6 +1,5 @@
 
using System; using System;
using System.Net.Http;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Types.InputFiles; using Telegram.Bot.Types.InputFiles;
@ -27,13 +26,7 @@ namespace Inotify.Sends.Products
{ {
var proxy = GetProxy(); var proxy = GetProxy();
var proxyHttpClientHandler = new HttpClientHandler var client = proxy == null ? new TelegramBotClient(Auth.BotToken) : new TelegramBotClient(Auth.BotToken, proxy);
{
Proxy = proxy,
UseProxy = true,
};
var httpClient = new HttpClient(proxyHttpClientHandler);
var client = proxy == null ? new TelegramBotClient(Auth.BotToken) : new TelegramBotClient(Auth.BotToken, httpClient);
var content = string.IsNullOrEmpty(message.Title) ? message.Title : message.Title + "\n" + message.Data; var content = string.IsNullOrEmpty(message.Title) ? message.Title : message.Title + "\n" + message.Data;
var isIMG = !string.IsNullOrEmpty(message.Title) && IsUrl(message.Title) && IsImage(message.Title) && string.IsNullOrEmpty(message.Data); var isIMG = !string.IsNullOrEmpty(message.Title) && IsUrl(message.Title) && IsImage(message.Title) && string.IsNullOrEmpty(message.Data);
if (isIMG) if (isIMG)

View File

@ -2,24 +2,9 @@
{ {
public class SendMessage public class SendMessage
{ {
public SendMessage()
{
}
public SendMessage(SendMessage sendMessage)
{
Token = sendMessage.Token;
Title = sendMessage.Title;
Data = sendMessage.Data;
Key = sendMessage.Key;
}
public string Token; public string Token;
public string Title; public string Title;
public string? Data; public string? Data;
public string? Key; public string? Key;
} }
} }

View File

@ -178,17 +178,15 @@ namespace Inotify.Sends
var result = sendMessageMethod.Invoke(sendMethodTemplateActor, new object[] { message }); var result = sendMessageMethod.Invoke(sendMethodTemplateActor, new object[] { message });
if (result != null) if (result != null)
{ {
var logMessage = new SendMessage(message); m_analyseMessages.Add(message);
logMessage.Key = authInfo.Key;
m_analyseMessages.Add(logMessage);
if ((bool)result) if ((bool)result)
{ {
OnSendSucessed?.Invoke(this, logMessage); OnSendSucessed?.Invoke(this, message);
continue; continue;
} }
else else
{ {
OnSendFailed?.Invoke(this, logMessage); OnSendFailed?.Invoke(this, message);
} }
} }
@ -215,7 +213,7 @@ namespace Inotify.Sends
{ {
var message = m_analyseMessages.Take(); var message = m_analyseMessages.Take();
var date = DateTime.Now.ToString("yyyyMMdd"); var date = DateTime.Now.ToString("yyyyMMdd");
var authData = DBManager.Instance.GetSendAuthInfo(message.Key, out string temeplateId); var authData = DBManager.Instance.GetSendAuthInfo(message.Token, out string temeplateId);
if (temeplateId != null) if (temeplateId != null)
{ {

View File

@ -203,7 +203,7 @@ namespace Inotify.Sends
if (proxyurl != null) if (proxyurl != null)
{ {
WebProxy proxy = new WebProxy() WebProxy proxy = new WebProxy
{ {
Address = new Uri(proxyurl) Address = new Uri(proxyurl)
}; };
@ -237,12 +237,9 @@ namespace Inotify.Sends
{ {
if (IsUrl(url) && IsImage(url)) if (IsUrl(url) && IsImage(url))
{ {
using (WebClient mywebclient = new WebClient()) WebClient mywebclient = new WebClient();
{
return mywebclient.DownloadData(url); return mywebclient.DownloadData(url);
} }
}
return null; return null;
} }
} }

View File

@ -53,7 +53,7 @@ namespace Inotify
{ {
OnAuthenticationFailed = context => OnAuthenticationFailed = context =>
{ {
var payload = JsonConvert.SerializeObject(new { message = "认证失败", code = 403 }); var payload = JsonConvert.SerializeObject(new { message = "ÈÏ֤ʧ°Ü", code = 403 });
context.Response.ContentType = "application/json"; context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status200OK; context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.WriteAsync(payload); context.Response.WriteAsync(payload);
@ -62,7 +62,7 @@ namespace Inotify
}, },
OnForbidden = context => OnForbidden = context =>
{ {
var payload = JsonConvert.SerializeObject(new { message = "未经授权", code = 405 }); var payload = JsonConvert.SerializeObject(new { message = "δ¾­ÊÚȨ", code = 405 });
context.Response.ContentType = "application/json"; context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status200OK; context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.WriteAsync(payload); context.Response.WriteAsync(payload);
@ -121,7 +121,7 @@ namespace Inotify
{ {
var groups = match.Groups; var groups = match.Groups;
rewriteContext.HttpContext.Request.Path = @"/api/send"; rewriteContext.HttpContext.Request.Path = @"/api/send";
rewriteContext.HttpContext.Request.QueryString = new QueryString($"?token={groups[2]}&title={groups[3]}&data={groups[4]}"); rewriteContext.HttpContext.Request.QueryString = new QueryString($"?token={groups[2]}&title={groups[3]}&date={groups[4]}");
} }
else else
{ {

View File

@ -1,11 +1,13 @@
# inotify # inotify
[![docker](https://github.com/xpnas/inotify/actions/workflows/docker.yml/badge.svg?branch=master)](https://github.com/xpnas/inotify/actions/workflows/docker.yml) [![docker](https://github.com/xpnas/inotify/actions/workflows/docker.yml/badge.svg)](https://github.com/xpnas/inotify/actions/workflows/docker.yml)
一个简易的消息通知系统,支持企业微信、电报机器人、邮件推送 一个简易的消息通知系统,支持企业微信、电报机器人、邮件推送
类似Server酱、容易扩展 类似Server酱、容易扩展
示例站点https://inotify.cf
## 功能支持 ## 功能支持
- [x] 通道设置 - [x] 通道设置
@ -25,13 +27,27 @@
- [x] 飞书群机器人 - [x] 飞书群机器人
- [x] 自定义 - [x] 自定义
## 更新日志
* V1.0
* 支持企业微信应用、电报、SMTP消息
* V2.0.0.1
* 支持自定义Get、POST
* V2.0.0.2
* 支持BARK
* V2.0.0.3
* 支持钉钉群消息
* 支持飞书群消息
* V2.0.0.4
* 支持通道独立消息推送
## 使用方法 ## 使用方法
1. Docker安装 1. Docker安装
* 发布版 * 稳定版V1.0
``` ```
docker run --name=inotify -d -p 8000:80 -v inotify_data:/inotify_data --restart=always xpnas/inotify:latest docker run --name=inotify -d -p 8000:80 -v inotify_data:/inotify_data --restart=always xpnas/inotify:latest
``` ```
* 开发版 * 开发版V2.0.0.4
``` ```
docker run --name=inotify -d -p 8000:80 -v inotify_data:/inotify_data --restart=always xpnas/inotify:master docker run --name=inotify -d -p 8000:80 -v inotify_data:/inotify_data --restart=always xpnas/inotify:master
``` ```
@ -56,11 +72,11 @@
## 系统截图 ## 系统截图
![](../public/A.png) ![](../master/public/A.png)
![](../public/B.png) ![](../master/public/B.png)
![](../public/C.png) ![](../master/public/C.png)
![](../public/D.png) ![](../master/public/D.png)

View File

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31129.286
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Inotify", "Inotify\Inotify.csproj", "{EEDAC4E1-2C4F-4400-B2AE-E7027EE83C83}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EEDAC4E1-2C4F-4400-B2AE-E7027EE83C83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EEDAC4E1-2C4F-4400-B2AE-E7027EE83C83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EEDAC4E1-2C4F-4400-B2AE-E7027EE83C83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EEDAC4E1-2C4F-4400-B2AE-E7027EE83C83}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E659C816-7213-4399-9AB3-0E575CAD9C97}
EndGlobalSection
EndGlobal