├── .gitignore
├── LICENSE
├── SaveAllTheHomework.sln
├── SaveAllTheHomework
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── Resources
│ └── zh-CN
│ │ ├── Resources.Designer.cs
│ │ └── Resources.resw
├── Ribbons
│ ├── MainRibbon.cs
│ └── MainRibbon.xml
├── SaveAllTheHomework.csproj
├── Source
│ ├── DesktopNotifications
│ │ └── DesktopNotificationManagerCompat.cs
│ ├── HomeworkBot.cs
│ ├── HomeworkItem.cs
│ └── Notification
│ │ └── WarningNotification.cs
├── ThisAddIn.Designer.cs
├── ThisAddIn.Designer.xml
└── ThisAddIn.cs
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 |
33 | # Visual Studio 2015/2017 cache/options directory
34 | .vs/
35 | # Uncomment if you have tasks that create the project's static files in wwwroot
36 | #wwwroot/
37 |
38 | # Visual Studio 2017 auto generated files
39 | Generated\ Files/
40 |
41 | # MSTest test Results
42 | [Tt]est[Rr]esult*/
43 | [Bb]uild[Ll]og.*
44 |
45 | # NUNIT
46 | *.VisualState.xml
47 | TestResult.xml
48 |
49 | # Build Results of an ATL Project
50 | [Dd]ebugPS/
51 | [Rr]eleasePS/
52 | dlldata.c
53 |
54 | # Benchmark Results
55 | BenchmarkDotNet.Artifacts/
56 |
57 | # .NET Core
58 | project.lock.json
59 | project.fragment.lock.json
60 | artifacts/
61 |
62 | # StyleCop
63 | StyleCopReport.xml
64 |
65 | # Files built by Visual Studio
66 | *_i.c
67 | *_p.c
68 | *_h.h
69 | *.ilk
70 | *.meta
71 | *.obj
72 | *.iobj
73 | *.pch
74 | *.pdb
75 | *.ipdb
76 | *.pgc
77 | *.pgd
78 | *.rsp
79 | *.sbr
80 | *.tlb
81 | *.tli
82 | *.tlh
83 | *.tmp
84 | *.tmp_proj
85 | *_wpftmp.csproj
86 | *.log
87 | *.vspscc
88 | *.vssscc
89 | .builds
90 | *.pidb
91 | *.svclog
92 | *.scc
93 |
94 | # Chutzpah Test files
95 | _Chutzpah*
96 |
97 | # Visual C++ cache files
98 | ipch/
99 | *.aps
100 | *.ncb
101 | *.opendb
102 | *.opensdf
103 | *.sdf
104 | *.cachefile
105 | *.VC.db
106 | *.VC.VC.opendb
107 |
108 | # Visual Studio profiler
109 | *.psess
110 | *.vsp
111 | *.vspx
112 | *.sap
113 |
114 | # Visual Studio Trace Files
115 | *.e2e
116 |
117 | # TFS 2012 Local Workspace
118 | $tf/
119 |
120 | # Guidance Automation Toolkit
121 | *.gpState
122 |
123 | # ReSharper is a .NET coding add-in
124 | _ReSharper*/
125 | *.[Rr]e[Ss]harper
126 | *.DotSettings.user
127 |
128 | # JustCode is a .NET coding add-in
129 | .JustCode
130 |
131 | # TeamCity is a build add-in
132 | _TeamCity*
133 |
134 | # DotCover is a Code Coverage Tool
135 | *.dotCover
136 |
137 | # AxoCover is a Code Coverage Tool
138 | .axoCover/*
139 | !.axoCover/settings.json
140 |
141 | # Visual Studio code coverage results
142 | *.coverage
143 | *.coveragexml
144 |
145 | # NCrunch
146 | _NCrunch_*
147 | .*crunch*.local.xml
148 | nCrunchTemp_*
149 |
150 | # MightyMoose
151 | *.mm.*
152 | AutoTest.Net/
153 |
154 | # Web workbench (sass)
155 | .sass-cache/
156 |
157 | # Installshield output folder
158 | [Ee]xpress/
159 |
160 | # DocProject is a documentation generator add-in
161 | DocProject/buildhelp/
162 | DocProject/Help/*.HxT
163 | DocProject/Help/*.HxC
164 | DocProject/Help/*.hhc
165 | DocProject/Help/*.hhk
166 | DocProject/Help/*.hhp
167 | DocProject/Help/Html2
168 | DocProject/Help/html
169 |
170 | # Click-Once directory
171 | publish/
172 |
173 | # Publish Web Output
174 | *.[Pp]ublish.xml
175 | *.azurePubxml
176 | # Note: Comment the next line if you want to checkin your web deploy settings,
177 | # but database connection strings (with potential passwords) will be unencrypted
178 | *.pubxml
179 | *.publishproj
180 |
181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
182 | # checkin your Azure Web App publish settings, but sensitive information contained
183 | # in these scripts will be unencrypted
184 | PublishScripts/
185 |
186 | # NuGet Packages
187 | *.nupkg
188 | # The packages folder can be ignored because of Package Restore
189 | **/[Pp]ackages/*
190 | # except build/, which is used as an MSBuild target.
191 | !**/[Pp]ackages/build/
192 | # Uncomment if necessary however generally it will be regenerated when needed
193 | #!**/[Pp]ackages/repositories.config
194 | # NuGet v3's project.json files produces more ignorable files
195 | *.nuget.props
196 | *.nuget.targets
197 |
198 | # Microsoft Azure Build Output
199 | csx/
200 | *.build.csdef
201 |
202 | # Microsoft Azure Emulator
203 | ecf/
204 | rcf/
205 |
206 | # Windows Store app package directories and files
207 | AppPackages/
208 | BundleArtifacts/
209 | Package.StoreAssociation.xml
210 | _pkginfo.txt
211 | *.appx
212 | *.appxbundle
213 | *.appxupload
214 |
215 | # Visual Studio cache files
216 | # files ending in .cache can be ignored
217 | *.[Cc]ache
218 | # but keep track of directories ending in .cache
219 | !?*.[Cc]ache/
220 |
221 | # Others
222 | ClientBin/
223 | ~$*
224 | *~
225 | *.dbmdl
226 | *.dbproj.schemaview
227 | *.jfm
228 | *.pfx
229 | *.publishsettings
230 | orleans.codegen.cs
231 |
232 | # Including strong name files can present a security risk
233 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
234 | #*.snk
235 |
236 | # Since there are multiple workflows, uncomment next line to ignore bower_components
237 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
238 | #bower_components/
239 |
240 | # RIA/Silverlight projects
241 | Generated_Code/
242 |
243 | # Backup & report files from converting an old project file
244 | # to a newer Visual Studio version. Backup files are not needed,
245 | # because we have git ;-)
246 | _UpgradeReport_Files/
247 | Backup*/
248 | UpgradeLog*.XML
249 | UpgradeLog*.htm
250 | ServiceFabricBackup/
251 | *.rptproj.bak
252 |
253 | # SQL Server files
254 | *.mdf
255 | *.ldf
256 | *.ndf
257 |
258 | # Business Intelligence projects
259 | *.rdl.data
260 | *.bim.layout
261 | *.bim_*.settings
262 | *.rptproj.rsuser
263 | *- Backup*.rdl
264 |
265 | # Microsoft Fakes
266 | FakesAssemblies/
267 |
268 | # GhostDoc plugin setting file
269 | *.GhostDoc.xml
270 |
271 | # Node.js Tools for Visual Studio
272 | .ntvs_analysis.dat
273 | node_modules/
274 |
275 | # Visual Studio 6 build log
276 | *.plg
277 |
278 | # Visual Studio 6 workspace options file
279 | *.opt
280 |
281 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
282 | *.vbw
283 |
284 | # Visual Studio LightSwitch build output
285 | **/*.HTMLClient/GeneratedArtifacts
286 | **/*.DesktopClient/GeneratedArtifacts
287 | **/*.DesktopClient/ModelManifest.xml
288 | **/*.Server/GeneratedArtifacts
289 | **/*.Server/ModelManifest.xml
290 | _Pvt_Extensions
291 |
292 | # Paket dependency manager
293 | .paket/paket.exe
294 | paket-files/
295 |
296 | # FAKE - F# Make
297 | .fake/
298 |
299 | # CodeRush personal settings
300 | .cr/personal
301 |
302 | # Python Tools for Visual Studio (PTVS)
303 | __pycache__/
304 | *.pyc
305 |
306 | # Cake - Uncomment if you are using it
307 | # tools/**
308 | # !tools/packages.config
309 |
310 | # Tabs Studio
311 | *.tss
312 |
313 | # Telerik's JustMock configuration file
314 | *.jmconfig
315 |
316 | # BizTalk build output
317 | *.btp.cs
318 | *.btm.cs
319 | *.odx.cs
320 | *.xsd.cs
321 |
322 | # OpenCover UI analysis results
323 | OpenCover/
324 |
325 | # Azure Stream Analytics local run output
326 | ASALocalRun/
327 |
328 | # MSBuild Binary and Structured Log
329 | *.binlog
330 |
331 | # NVidia Nsight GPU debugger configuration file
332 | *.nvuser
333 |
334 | # MFractors (Xamarin productivity tool) working folder
335 | .mfractor/
336 |
337 | # Local History for Visual Studio
338 | .localhistory/
339 |
340 | # BeatPulse healthcheck temp database
341 | healthchecksdb
342 |
343 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
344 | MigrationBackup/
345 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Eric_Lian
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SaveAllTheHomework.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29230.47
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaveAllTheHomework", "SaveAllTheHomework\SaveAllTheHomework.csproj", "{80D48821-E1C0-419D-BA3F-3A661C723DDF}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {80D48821-E1C0-419D-BA3F-3A661C723DDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {80D48821-E1C0-419D-BA3F-3A661C723DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {80D48821-E1C0-419D-BA3F-3A661C723DDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {80D48821-E1C0-419D-BA3F-3A661C723DDF}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {BDA54E44-105C-4409-9715-3F183939B1AC}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using System.Security;
5 |
6 | // General Information about an assembly is controlled through the following
7 | // set of attributes. Change these attribute values to modify the information
8 | // associated with an assembly.
9 | [assembly: AssemblyTitle("SaveAllTheHomework")]
10 | [assembly: AssemblyDescription("")]
11 | [assembly: AssemblyConfiguration("")]
12 | [assembly: AssemblyCompany("")]
13 | [assembly: AssemblyProduct("SaveAllTheHomework")]
14 | [assembly: AssemblyCopyright("Copyright © 2019")]
15 | [assembly: AssemblyTrademark("")]
16 | [assembly: AssemblyCulture("")]
17 |
18 | // Setting ComVisible to false makes the types in this assembly not visible
19 | // to COM components. If you need to access a type in this assembly from
20 | // COM, set the ComVisible attribute to true on that type.
21 | [assembly: ComVisible(false)]
22 |
23 | // The following GUID is for the ID of the typelib if this project is exposed to COM
24 | [assembly: Guid("295fe35e-15b4-4935-9a47-8efc747364ec")]
25 |
26 | // Version information for an assembly consists of the following four values:
27 | //
28 | // Major Version
29 | // Minor Version
30 | // Build Number
31 | // Revision
32 | //
33 | // You can specify all the values or you can default the Build and Revision Numbers
34 | // by using the '*' as shown below:
35 | // [assembly: AssemblyVersion("1.0.*")]
36 | [assembly: AssemblyVersion("1.0.0.0")]
37 | [assembly: AssemblyFileVersion("1.0.0.0")]
38 |
39 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SaveAllTheHomework.Properties {
12 |
13 |
14 | ///
15 | /// A strongly-typed resource class, for looking up localized strings, etc.
16 | ///
17 | // This class was auto-generated by the StronglyTypedResourceBuilder
18 | // class via a tool like ResGen or Visual Studio.
19 | // To add or remove a member, edit your .ResX file then rerun ResGen
20 | // with the /str option, or rebuild your VS project.
21 |
22 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
23 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
24 | internal class Resources {
25 |
26 | private static global::System.Resources.ResourceManager resourceMan;
27 |
28 | private static global::System.Globalization.CultureInfo resourceCulture;
29 |
30 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
31 | internal Resources() {
32 | }
33 |
34 | ///
35 | /// Returns the cached ResourceManager instance used by this class.
36 | ///
37 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
38 | internal static global::System.Resources.ResourceManager ResourceManager {
39 | get {
40 | if (object.ReferenceEquals(resourceMan, null)) {
41 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SaveAllTheHomework.Properties.Resources", typeof(Resources).Assembly);
42 | resourceMan = temp;
43 | }
44 | return resourceMan;
45 | }
46 | }
47 |
48 | ///
49 | /// Overrides the current thread's CurrentUICulture property for all
50 | /// resource lookups using this strongly typed resource class.
51 | ///
52 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
53 | internal static global::System.Globalization.CultureInfo Culture {
54 | get {
55 | return resourceCulture;
56 | }
57 | set {
58 | resourceCulture = value;
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SaveAllTheHomework.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Resources/zh-CN/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SaveAllTheHomework.Resources.zh_CN {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SaveAllTheHomework.Resources.zh_CN.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Resources/zh-CN/Resources.resw:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 | text/microsoft-resx
91 |
92 |
93 | 1.3
94 |
95 |
96 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
97 |
98 |
99 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
100 |
101 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Ribbons/MainRibbon.cs:
--------------------------------------------------------------------------------
1 | using SaveAllTheHomework.Source;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Runtime.InteropServices;
8 | using System.Text;
9 | using Office = Microsoft.Office.Core;
10 |
11 | // TODO: Follow these steps to enable the Ribbon (XML) item:
12 |
13 | // 1: Copy the following code block into the ThisAddin, ThisWorkbook, or ThisDocument class.
14 |
15 | // protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
16 | // {
17 | // return new MainRibbon();
18 | // }
19 |
20 | // 2. Create callback methods in the "Ribbon Callbacks" region of this class to handle user
21 | // actions, such as clicking a button. Note: if you have exported this Ribbon from the Ribbon designer,
22 | // move your code from the event handlers to the callback methods and modify the code to work with the
23 | // Ribbon extensibility (RibbonX) programming model.
24 |
25 | // 3. Assign attributes to the control tags in the Ribbon XML file to identify the appropriate callback methods in your code.
26 |
27 | // For more information, see the Ribbon XML documentation in the Visual Studio Tools for Office Help.
28 |
29 |
30 | namespace SaveAllTheHomework.Ribbons
31 | {
32 | [ComVisible(true)]
33 | public class MainRibbon : Office.IRibbonExtensibility
34 | {
35 | private Office.IRibbonUI ribbon;
36 | public HomeworkBot aHomeworkBot;
37 |
38 | public MainRibbon()
39 | {
40 | }
41 |
42 | #region IRibbonExtensibility Members
43 |
44 | public string GetCustomUI(string ribbonID)
45 | {
46 | return GetResourceText("SaveAllTheHomework.Ribbons.MainRibbon.xml");
47 | }
48 |
49 | #endregion
50 |
51 | #region Ribbon Callbacks
52 | //Create callback methods here. For more information about adding callback methods, visit https://go.microsoft.com/fwlink/?LinkID=271226
53 |
54 | public void Ribbon_Load(Office.IRibbonUI ribbonUI)
55 | {
56 | this.ribbon = ribbonUI;
57 | }
58 |
59 | public void OnSaveAllHomework(Office.IRibbonControl control) {
60 | aHomeworkBot.SaveAllHomework();
61 | }
62 |
63 | #endregion
64 |
65 | #region Helpers
66 |
67 | private static string GetResourceText(string resourceName)
68 | {
69 | Assembly asm = Assembly.GetExecutingAssembly();
70 | string[] resourceNames = asm.GetManifestResourceNames();
71 | for (int i = 0; i < resourceNames.Length; ++i)
72 | {
73 | if (string.Compare(resourceName, resourceNames[i], StringComparison.OrdinalIgnoreCase) == 0)
74 | {
75 | using (StreamReader resourceReader = new StreamReader(asm.GetManifestResourceStream(resourceNames[i])))
76 | {
77 | if (resourceReader != null)
78 | {
79 | return resourceReader.ReadToEnd();
80 | }
81 | }
82 | }
83 | }
84 | return null;
85 | }
86 |
87 | #endregion
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Ribbons/MainRibbon.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/SaveAllTheHomework.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
20 |
21 | {BAA0C2D2-18E2-41B9-852F-F413020CAA33};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
22 | Debug
23 | AnyCPU
24 | {80D48821-E1C0-419D-BA3F-3A661C723DDF}
25 | Library
26 | false
27 | SaveAllTheHomework
28 | SaveAllTheHomework
29 | 3
30 | v4.7.2
31 | VSTO40
32 | true
33 | HomeSite
34 |
35 |
36 |
37 | False
38 | Microsoft Visual Studio 2010 Tools for Office Runtime %28x86 and x64%29
39 | true
40 |
41 |
42 |
43 |
47 | Outlook
48 |
49 |
65 |
66 | true
67 | full
68 | false
69 | bin\Debug\
70 | false
71 | $(DefineConstants);DEBUG;TRACE
72 | 4
73 |
74 |
90 |
91 | pdbonly
92 | true
93 | bin\Release\
94 | false
95 | $(DefineConstants);TRACE
96 | 4
97 |
98 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | False
116 |
117 |
118 | False
119 |
120 |
121 | False
122 |
123 |
124 | False
125 |
126 |
127 | False
128 |
129 |
130 |
131 |
132 | True
133 |
134 |
135 | True
136 |
137 |
138 |
139 |
140 | False
141 | true
142 |
143 |
144 | False
145 | true
146 |
147 |
148 | False
149 |
150 |
151 |
161 |
162 |
163 | Code
164 |
165 |
166 | ResXFileCodeGenerator
167 | Resources.Designer.cs
168 | Designer
169 |
170 |
171 | True
172 | Resources.resx
173 |
174 |
175 | SettingsSingleFileGenerator
176 | Settings.Designer.cs
177 |
178 |
179 | True
180 | Settings.settings
181 |
182 |
183 | True
184 | True
185 | Resources.resw
186 |
187 |
188 |
189 |
190 |
191 | Code
192 |
193 |
194 | ResXFileCodeGenerator
195 | Resources.Designer.cs
196 |
197 |
198 |
199 | ThisAddIn.cs
200 |
201 |
202 | ThisAddIn.Designer.xml
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 | 10.0
211 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
212 |
213 |
214 | true
215 |
216 |
217 | SaveAllTheHomework_TemporaryKey.pfx
218 |
219 |
220 | D0DB197962EAF7ED19DE7FB15AD8112715409AAD
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Source/DesktopNotifications/DesktopNotificationManagerCompat.cs:
--------------------------------------------------------------------------------
1 | // ******************************************************************
2 | // Copyright (c) Microsoft. All rights reserved.
3 | // This code is licensed under the MIT License (MIT).
4 | // THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
5 | // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
7 | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
8 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
9 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
10 | // THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
11 | // ******************************************************************
12 |
13 | using System;
14 | using System.Collections;
15 | using System.Collections.Generic;
16 | using System.Diagnostics;
17 | using System.Linq;
18 | using System.Runtime.InteropServices;
19 | using System.Text;
20 | using Windows.UI.Notifications;
21 |
22 | namespace SaveAllTheHomework.Source.DesktopNotifications
23 | {
24 | public class DesktopNotificationManagerCompat
25 | {
26 | public const string TOAST_ACTIVATED_LAUNCH_ARG = "-ToastActivated";
27 |
28 | private static bool _registeredAumidAndComServer;
29 | private static string _aumid;
30 | private static bool _registeredActivator;
31 |
32 | ///
33 | /// If not running under the Desktop Bridge, you must call this method to register your AUMID with the Compat library and to
34 | /// register your COM CLSID and EXE in LocalServer32 registry. Feel free to call this regardless, and we will no-op if running
35 | /// under Desktop Bridge. Call this upon application startup, before calling any other APIs.
36 | ///
37 | /// An AUMID that uniquely identifies your application.
38 | public static void RegisterAumidAndComServer(string aumid)
39 | where T : NotificationActivator
40 | {
41 | if (string.IsNullOrWhiteSpace(aumid))
42 | {
43 | throw new ArgumentException("You must provide an AUMID.", nameof(aumid));
44 | }
45 |
46 | // If running as Desktop Bridge
47 | if (DesktopBridgeHelpers.IsRunningAsUwp())
48 | {
49 | // Clear the AUMID since Desktop Bridge doesn't use it, and then we're done.
50 | // Desktop Bridge apps are registered with platform through their manifest.
51 | // Their LocalServer32 key is also registered through their manifest.
52 | _aumid = null;
53 | _registeredAumidAndComServer = true;
54 | return;
55 | }
56 |
57 | _aumid = aumid;
58 |
59 | String exePath = Process.GetCurrentProcess().MainModule.FileName;
60 | RegisterComServer(exePath);
61 |
62 | _registeredAumidAndComServer = true;
63 | }
64 |
65 | private static void RegisterComServer(String exePath)
66 | where T : NotificationActivator
67 | {
68 | // We register the EXE to start up when the notification is activated
69 | string regString = String.Format("SOFTWARE\\Classes\\CLSID\\{{{0}}}\\LocalServer32", typeof(T).GUID);
70 | var key = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(regString);
71 |
72 | // Include a flag so we know this was a toast activation and should wait for COM to process
73 | // We also wrap EXE path in quotes for extra security
74 | key.SetValue(null, '"' + exePath + '"' + " " + TOAST_ACTIVATED_LAUNCH_ARG);
75 | }
76 |
77 | ///
78 | /// Registers the activator type as a COM server client so that Windows can launch your activator.
79 | ///
80 | /// Your implementation of NotificationActivator. Must have GUID and ComVisible attributes on class.
81 | public static void RegisterActivator()
82 | where T : NotificationActivator
83 | {
84 | // Register type
85 | var regService = new RegistrationServices();
86 |
87 | regService.RegisterTypeForComClients(
88 | typeof(T),
89 | RegistrationClassContext.LocalServer,
90 | RegistrationConnectionType.MultipleUse);
91 |
92 | _registeredActivator = true;
93 | }
94 |
95 | ///
96 | /// Creates a toast notifier. You must have called first (and also if you're a classic Win32 app), or this will throw an exception.
97 | ///
98 | ///
99 | public static ToastNotifier CreateToastNotifier()
100 | {
101 | EnsureRegistered();
102 |
103 | if (_aumid != null)
104 | {
105 | // Non-Desktop Bridge
106 | return ToastNotificationManager.CreateToastNotifier(_aumid);
107 | }
108 | else
109 | {
110 | // Desktop Bridge
111 | return ToastNotificationManager.CreateToastNotifier();
112 | }
113 | }
114 |
115 | ///
116 | /// Gets the object. You must have called first (and also if you're a classic Win32 app), or this will throw an exception.
117 | ///
118 | public static DesktopNotificationHistoryCompat History
119 | {
120 | get
121 | {
122 | EnsureRegistered();
123 |
124 | return new DesktopNotificationHistoryCompat(_aumid);
125 | }
126 | }
127 |
128 | private static void EnsureRegistered()
129 | {
130 | // If not registered AUMID yet
131 | if (!_registeredAumidAndComServer)
132 | {
133 | // Check if Desktop Bridge
134 | if (DesktopBridgeHelpers.IsRunningAsUwp())
135 | {
136 | // Implicitly registered, all good!
137 | _registeredAumidAndComServer = true;
138 | }
139 |
140 | else
141 | {
142 | // Otherwise, incorrect usage
143 | throw new Exception("You must call RegisterAumidAndComServer first.");
144 | }
145 | }
146 |
147 | // If not registered activator yet
148 | if (!_registeredActivator)
149 | {
150 | // Incorrect usage
151 | throw new Exception("You must call RegisterActivator first.");
152 | }
153 | }
154 |
155 | ///
156 | /// Gets a boolean representing whether http images can be used within toasts. This is true if running under Desktop Bridge.
157 | ///
158 | public static bool CanUseHttpImages { get { return DesktopBridgeHelpers.IsRunningAsUwp(); } }
159 |
160 | ///
161 | /// Code from https://github.com/qmatteoq/DesktopBridgeHelpers/edit/master/DesktopBridge.Helpers/Helpers.cs
162 | ///
163 | private class DesktopBridgeHelpers
164 | {
165 | const long APPMODEL_ERROR_NO_PACKAGE = 15700L;
166 |
167 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
168 | static extern int GetCurrentPackageFullName(ref int packageFullNameLength, StringBuilder packageFullName);
169 |
170 | private static bool? _isRunningAsUwp;
171 | public static bool IsRunningAsUwp()
172 | {
173 | if (_isRunningAsUwp == null)
174 | {
175 | if (IsWindows7OrLower)
176 | {
177 | _isRunningAsUwp = false;
178 | }
179 | else
180 | {
181 | int length = 0;
182 | StringBuilder sb = new StringBuilder(0);
183 | int result = GetCurrentPackageFullName(ref length, sb);
184 |
185 | sb = new StringBuilder(length);
186 | result = GetCurrentPackageFullName(ref length, sb);
187 |
188 | _isRunningAsUwp = result != APPMODEL_ERROR_NO_PACKAGE;
189 | }
190 | }
191 |
192 | return _isRunningAsUwp.Value;
193 | }
194 |
195 | private static bool IsWindows7OrLower
196 | {
197 | get
198 | {
199 | int versionMajor = Environment.OSVersion.Version.Major;
200 | int versionMinor = Environment.OSVersion.Version.Minor;
201 | double version = versionMajor + (double)versionMinor / 10;
202 | return version <= 6.1;
203 | }
204 | }
205 | }
206 | }
207 |
208 | ///
209 | /// Manages the toast notifications for an app including the ability the clear all toast history and removing individual toasts.
210 | ///
211 | public sealed class DesktopNotificationHistoryCompat
212 | {
213 | private string _aumid;
214 | private ToastNotificationHistory _history;
215 |
216 | ///
217 | /// Do not call this. Instead, call to obtain an instance.
218 | ///
219 | ///
220 | internal DesktopNotificationHistoryCompat(string aumid)
221 | {
222 | _aumid = aumid;
223 | _history = ToastNotificationManager.History;
224 | }
225 |
226 | ///
227 | /// Removes all notifications sent by this app from action center.
228 | ///
229 | public void Clear()
230 | {
231 | if (_aumid != null)
232 | {
233 | _history.Clear(_aumid);
234 | }
235 | else
236 | {
237 | _history.Clear();
238 | }
239 | }
240 |
241 | ///
242 | /// Gets all notifications sent by this app that are currently still in Action Center.
243 | ///
244 | /// A collection of toasts.
245 | public IReadOnlyList GetHistory()
246 | {
247 | return _aumid != null ? _history.GetHistory(_aumid) : _history.GetHistory();
248 | }
249 |
250 | ///
251 | /// Removes an individual toast, with the specified tag label, from action center.
252 | ///
253 | /// The tag label of the toast notification to be removed.
254 | public void Remove(string tag)
255 | {
256 | if (_aumid != null)
257 | {
258 | _history.Remove(tag, string.Empty, _aumid);
259 | }
260 | else
261 | {
262 | _history.Remove(tag);
263 | }
264 | }
265 |
266 | ///
267 | /// Removes a toast notification from the action using the notification's tag and group labels.
268 | ///
269 | /// The tag label of the toast notification to be removed.
270 | /// The group label of the toast notification to be removed.
271 | public void Remove(string tag, string group)
272 | {
273 | if (_aumid != null)
274 | {
275 | _history.Remove(tag, group, _aumid);
276 | }
277 | else
278 | {
279 | _history.Remove(tag, group);
280 | }
281 | }
282 |
283 | ///
284 | /// Removes a group of toast notifications, identified by the specified group label, from action center.
285 | ///
286 | /// The group label of the toast notifications to be removed.
287 | public void RemoveGroup(string group)
288 | {
289 | if (_aumid != null)
290 | {
291 | _history.RemoveGroup(group, _aumid);
292 | }
293 | else
294 | {
295 | _history.RemoveGroup(group);
296 | }
297 | }
298 | }
299 |
300 | ///
301 | /// Apps must implement this activator to handle notification activation.
302 | ///
303 | public abstract class NotificationActivator : NotificationActivator.INotificationActivationCallback
304 | {
305 | public void Activate(string appUserModelId, string invokedArgs, NOTIFICATION_USER_INPUT_DATA[] data, uint dataCount)
306 | {
307 | OnActivated(invokedArgs, new NotificationUserInput(data), appUserModelId);
308 | }
309 |
310 | ///
311 | /// This method will be called when the user clicks on a foreground or background activation on a toast. Parent app must implement this method.
312 | ///
313 | /// The arguments from the original notification. This is either the launch argument if the user clicked the body of your toast, or the arguments from a button on your toast.
314 | /// Text and selection values that the user entered in your toast.
315 | /// Your AUMID.
316 | public abstract void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId);
317 |
318 | // These are the new APIs for Windows 10
319 | #region NewAPIs
320 | [StructLayout(LayoutKind.Sequential), Serializable]
321 | public struct NOTIFICATION_USER_INPUT_DATA
322 | {
323 | [MarshalAs(UnmanagedType.LPWStr)]
324 | public string Key;
325 |
326 | [MarshalAs(UnmanagedType.LPWStr)]
327 | public string Value;
328 | }
329 |
330 | [ComImport,
331 | Guid("53E31837-6600-4A81-9395-75CFFE746F94"), ComVisible(true),
332 | InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
333 | public interface INotificationActivationCallback
334 | {
335 | void Activate(
336 | [In, MarshalAs(UnmanagedType.LPWStr)]
337 | string appUserModelId,
338 | [In, MarshalAs(UnmanagedType.LPWStr)]
339 | string invokedArgs,
340 | [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)]
341 | NOTIFICATION_USER_INPUT_DATA[] data,
342 | [In, MarshalAs(UnmanagedType.U4)]
343 | uint dataCount);
344 | }
345 | #endregion
346 | }
347 |
348 | ///
349 | /// Text and selection values that the user entered on your notification. The Key is the ID of the input, and the Value is what the user entered.
350 | ///
351 | public class NotificationUserInput : IReadOnlyDictionary
352 | {
353 | private NotificationActivator.NOTIFICATION_USER_INPUT_DATA[] _data;
354 |
355 | internal NotificationUserInput(NotificationActivator.NOTIFICATION_USER_INPUT_DATA[] data)
356 | {
357 | _data = data;
358 | }
359 |
360 | public string this[string key] => _data.First(i => i.Key == key).Value;
361 |
362 | public IEnumerable Keys => _data.Select(i => i.Key);
363 |
364 | public IEnumerable Values => _data.Select(i => i.Value);
365 |
366 | public int Count => _data.Length;
367 |
368 | public bool ContainsKey(string key)
369 | {
370 | return _data.Any(i => i.Key == key);
371 | }
372 |
373 | public IEnumerator> GetEnumerator()
374 | {
375 | return _data.Select(i => new KeyValuePair(i.Key, i.Value)).GetEnumerator();
376 | }
377 |
378 | public bool TryGetValue(string key, out string value)
379 | {
380 | foreach (var item in _data)
381 | {
382 | if (item.Key == key)
383 | {
384 | value = item.Value;
385 | return true;
386 | }
387 | }
388 |
389 | value = null;
390 | return false;
391 | }
392 |
393 | IEnumerator IEnumerable.GetEnumerator()
394 | {
395 | return GetEnumerator();
396 | }
397 | }
398 | }
--------------------------------------------------------------------------------
/SaveAllTheHomework/Source/HomeworkBot.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | using Outlook = Microsoft.Office.Interop.Outlook;
8 | using Office = Microsoft.Office.Core;
9 | using System.Collections;
10 | using System.Windows.Forms;
11 | using System.Text.RegularExpressions;
12 | using System.IO;
13 |
14 | namespace SaveAllTheHomework.Source
15 | {
16 | public class HomeworkBot
17 | {
18 | public HomeworkBot()
19 | {
20 |
21 | }
22 |
23 | public int SaveAllHomework()
24 | {
25 |
26 |
27 |
28 | Outlook.MAPIFolder ActivedFolder = Globals.ThisAddIn.Application.ActiveExplorer().CurrentFolder;
29 |
30 | List HomeworkList = new List();
31 |
32 |
33 | foreach (dynamic myItem in ActivedFolder.Items)
34 | {
35 | /// myItem.Sender.Address System.String
36 | /// myItem.Attachments.Count
37 | ///
38 | /// myItem.CreationTime System.DateTime
39 | /// myItem.ConversationTopic System.String
40 | /// SentOn
41 |
42 | String sender = "";
43 |
44 | /// 尝试解析发件人
45 | try
46 | {
47 | sender = myItem.SenderEmailAddress;
48 | }
49 | catch (Exception e1) {
50 | try
51 | {
52 | sender = myItem.Sender.Address;
53 | }
54 | catch (Exception e2)
55 | {
56 |
57 | };
58 | };
59 |
60 |
61 | if (sender.Equals("")) {
62 | continue;
63 | }
64 |
65 | HomeworkItem aHomeworkItem = new HomeworkItem
66 | {
67 | Sender = sender
68 | };
69 |
70 | /// 识别学号
71 | // 尝试识别武汉大学邮箱凭此获得学号
72 | aHomeworkItem.StudentID = GetStudentIDFromWHUEmail(sender);
73 |
74 | // 尝试识别附件文件名凭此获得学号
75 | if (aHomeworkItem.StudentID == 0)
76 | {
77 | foreach (dynamic myAttachment in myItem.Attachments)
78 | {
79 | aHomeworkItem.StudentID = GetStudentIDFromString(myAttachment.FileName);
80 | if (aHomeworkItem.StudentID != 0) break;
81 | }
82 | }
83 |
84 | // 尝试识别邮件标题凭此获得学号
85 | if (aHomeworkItem.StudentID == 0)
86 | {
87 | aHomeworkItem.StudentID = GetStudentIDFromString(myItem.ConversationTopic);
88 | }
89 |
90 | // 如果都识别不到
91 | if (aHomeworkItem.StudentID == 0)
92 | {
93 | continue;
94 | };
95 |
96 | /// 邮件发送时间
97 | aHomeworkItem.SentOn = myItem.SentOn;
98 |
99 | // 如果没有附件
100 | if (myItem.Attachments.Count == 0)
101 | {
102 | continue;
103 | }
104 |
105 | /// 获取附件
106 | aHomeworkItem.Attachments = myItem.Attachments;
107 |
108 | HomeworkList.Add(aHomeworkItem);
109 |
110 |
111 | }
112 |
113 | // 对所有邮件按照
114 | // 学号第一次序升序
115 | // 邮件发送时间第二次序升序 排序
116 | HomeworkList.Sort();
117 |
118 | for (int i=0; i '9')) return false;
187 | }
188 |
189 | return true;
190 | }
191 |
192 | private long GetStudentIDFromWHUEmail(String sender)
193 | {
194 | String[] Email = sender.Split('@');
195 | if (!Email[1].ToLower().Equals("whu.edu.cn")) return 0;
196 | if (Email[0].Length != 13) return 0; // 2018 302 114514 // 4+3+6
197 |
198 | for (int i = 0; i < 13; i++)
199 | {
200 | if ((Email[0][i] < '0') || (Email[0][i] > '9')) return 0;
201 | }
202 |
203 | return long.Parse(Email[0]);
204 | }
205 |
206 | private long GetStudentIDFromString(String sender)
207 | {
208 | string pattern = @"\d{13}";
209 |
210 | foreach (Match match in Regex.Matches(sender, pattern))
211 | return long.Parse(match.Value);
212 |
213 | return 0;
214 | }
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/Source/HomeworkItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace SaveAllTheHomework.Source
5 | {
6 | internal class HomeworkItem : IComparable, IComparer
7 | {
8 | public String Sender = "";
9 | public long StudentID = 0;
10 | public dynamic Attachments = null;
11 | public DateTime SentOn = DateTime.UtcNow;
12 |
13 | public int Compare(HomeworkItem x, HomeworkItem y)
14 | {
15 | if (x.StudentID < y.StudentID) return -1;
16 | if (x.StudentID > y.StudentID) return 1;
17 | if (x.SentOn > y.SentOn) return 1;
18 | if (x.SentOn < y.SentOn) return -1;
19 |
20 | return 0;
21 | }
22 |
23 | public int CompareTo(object obj)
24 | {
25 | return Compare(this, (HomeworkItem)obj);
26 | }
27 |
28 | }
29 |
30 |
31 | }
--------------------------------------------------------------------------------
/SaveAllTheHomework/Source/Notification/WarningNotification.cs:
--------------------------------------------------------------------------------
1 |
2 | using SaveAllTheHomework.Source.DesktopNotifications;
3 |
4 | using System;
5 | using System.Collections;
6 | using System.Collections.Generic;
7 | using System.Diagnostics;
8 | using System.Linq;
9 | using System.Runtime.InteropServices;
10 | using System.Text;
11 | using Windows.UI.Notifications;
12 |
13 |
14 | namespace SaveAllTheHomework.Source.Notification
15 | {
16 | // The GUID CLSID must be unique to your app. Create a new GUID if copying this code.
17 | [ClassInterface(ClassInterfaceType.None)]
18 | [ComSourceInterfaces(typeof(INotificationActivationCallback))]
19 | [Guid("0603261d-a124-406d-9ff6-826a42cc6a48"), ComVisible(true)]
20 | public class WarningNotification : NotificationActivator
21 | {
22 | public override void OnActivated(string invokedArgs, NotificationUserInput userInput, string appUserModelId)
23 | {
24 | // TODO: Handle activation
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/SaveAllTheHomework/ThisAddIn.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | #pragma warning disable 414
12 | namespace SaveAllTheHomework {
13 |
14 |
15 | ///
16 | [Microsoft.VisualStudio.Tools.Applications.Runtime.StartupObjectAttribute(0)]
17 | [global::System.Security.Permissions.PermissionSetAttribute(global::System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
18 | public sealed partial class ThisAddIn : Microsoft.Office.Tools.Outlook.OutlookAddInBase {
19 |
20 | internal Microsoft.Office.Tools.CustomTaskPaneCollection CustomTaskPanes;
21 |
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
23 | private global::System.Object missing = global::System.Type.Missing;
24 |
25 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
26 | internal Microsoft.Office.Interop.Outlook.Application Application;
27 |
28 | ///
29 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
30 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
31 | public ThisAddIn(global::Microsoft.Office.Tools.Outlook.Factory factory, global::System.IServiceProvider serviceProvider) :
32 | base(factory, serviceProvider, "AddIn", "ThisAddIn") {
33 | Globals.Factory = factory;
34 | }
35 |
36 | ///
37 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
38 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
40 | protected override void Initialize() {
41 | base.Initialize();
42 | this.Application = this.GetHostItem(typeof(Microsoft.Office.Interop.Outlook.Application), "Application");
43 | Globals.ThisAddIn = this;
44 | global::System.Windows.Forms.Application.EnableVisualStyles();
45 | this.InitializeCachedData();
46 | this.InitializeControls();
47 | this.InitializeComponents();
48 | this.InitializeData();
49 | }
50 |
51 | ///
52 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
53 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
54 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
55 | protected override void FinishInitialization() {
56 | this.InternalStartup();
57 | this.OnStartup();
58 | }
59 |
60 | ///
61 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
62 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
63 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
64 | protected override void InitializeDataBindings() {
65 | this.BeginInitialization();
66 | this.BindToData();
67 | this.EndInitialization();
68 | }
69 |
70 | ///
71 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
72 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
73 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
74 | private void InitializeCachedData() {
75 | if ((this.DataHost == null)) {
76 | return;
77 | }
78 | if (this.DataHost.IsCacheInitialized) {
79 | this.DataHost.FillCachedData(this);
80 | }
81 | }
82 |
83 | ///
84 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
85 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
86 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
87 | private void InitializeData() {
88 | }
89 |
90 | ///
91 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
92 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
93 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
94 | private void BindToData() {
95 | }
96 |
97 | ///
98 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
99 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
100 | private void StartCaching(string MemberName) {
101 | this.DataHost.StartCaching(this, MemberName);
102 | }
103 |
104 | ///
105 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
106 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
107 | private void StopCaching(string MemberName) {
108 | this.DataHost.StopCaching(this, MemberName);
109 | }
110 |
111 | ///
112 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
113 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
114 | private bool IsCached(string MemberName) {
115 | return this.DataHost.IsCached(this, MemberName);
116 | }
117 |
118 | ///
119 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
120 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
121 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
122 | private void BeginInitialization() {
123 | this.BeginInit();
124 | this.CustomTaskPanes.BeginInit();
125 | }
126 |
127 | ///
128 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
129 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
130 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
131 | private void EndInitialization() {
132 | this.CustomTaskPanes.EndInit();
133 | this.EndInit();
134 | }
135 |
136 | ///
137 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
138 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
139 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
140 | private void InitializeControls() {
141 | this.CustomTaskPanes = Globals.Factory.CreateCustomTaskPaneCollection(null, null, "CustomTaskPanes", "CustomTaskPanes", this);
142 | }
143 |
144 | ///
145 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
146 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
147 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
148 | private void InitializeComponents() {
149 | }
150 |
151 | ///
152 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
153 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
154 | private bool NeedsFill(string MemberName) {
155 | return this.DataHost.NeedsFill(this, MemberName);
156 | }
157 |
158 | ///
159 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
160 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
161 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
162 | protected override void OnShutdown() {
163 | this.CustomTaskPanes.Dispose();
164 | base.OnShutdown();
165 | }
166 | }
167 |
168 | ///
169 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
170 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
171 | internal sealed partial class Globals {
172 |
173 | ///
174 | private Globals() {
175 | }
176 |
177 | private static ThisAddIn _ThisAddIn;
178 |
179 | private static global::Microsoft.Office.Tools.Outlook.Factory _factory;
180 |
181 | private static ThisRibbonCollection _ThisRibbonCollection;
182 |
183 | private static ThisFormRegionCollection _ThisFormRegionCollection;
184 |
185 | internal static ThisAddIn ThisAddIn {
186 | get {
187 | return _ThisAddIn;
188 | }
189 | set {
190 | if ((_ThisAddIn == null)) {
191 | _ThisAddIn = value;
192 | }
193 | else {
194 | throw new System.NotSupportedException();
195 | }
196 | }
197 | }
198 |
199 | internal static global::Microsoft.Office.Tools.Outlook.Factory Factory {
200 | get {
201 | return _factory;
202 | }
203 | set {
204 | if ((_factory == null)) {
205 | _factory = value;
206 | }
207 | else {
208 | throw new System.NotSupportedException();
209 | }
210 | }
211 | }
212 |
213 | internal static ThisRibbonCollection Ribbons {
214 | get {
215 | if ((_ThisRibbonCollection == null)) {
216 | _ThisRibbonCollection = new ThisRibbonCollection(_factory.GetRibbonFactory());
217 | }
218 | return _ThisRibbonCollection;
219 | }
220 | }
221 |
222 | internal static ThisFormRegionCollection FormRegions {
223 | get {
224 | if ((_ThisFormRegionCollection == null)) {
225 | _ThisFormRegionCollection = new ThisFormRegionCollection(Globals.ThisAddIn.GetFormRegions());
226 | }
227 | return _ThisFormRegionCollection;
228 | }
229 | }
230 | }
231 |
232 | ///
233 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
234 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Tools.Office.ProgrammingModel.dll", "16.0.0.0")]
235 | internal sealed partial class ThisRibbonCollection : Microsoft.Office.Tools.Ribbon.RibbonCollectionBase {
236 |
237 | ///
238 | internal ThisRibbonCollection(global::Microsoft.Office.Tools.Ribbon.RibbonFactory factory) :
239 | base(factory) {
240 | }
241 |
242 | internal ThisRibbonCollection this[Microsoft.Office.Interop.Outlook.Inspector inspector] {
243 | get {
244 | return this.GetRibbonContextCollection(inspector);
245 | }
246 | }
247 |
248 | internal ThisRibbonCollection this[Microsoft.Office.Interop.Outlook.Explorer explorer] {
249 | get {
250 | return this.GetRibbonContextCollection(explorer);
251 | }
252 | }
253 | }
254 |
255 | ///
256 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
257 | internal sealed partial class ThisFormRegionCollection : Microsoft.Office.Tools.Outlook.FormRegionCollectionBase {
258 |
259 | ///
260 | public ThisFormRegionCollection(System.Collections.Generic.IList list) :
261 | base(list) {
262 | }
263 |
264 | internal WindowFormRegionCollection this[Microsoft.Office.Interop.Outlook.Explorer explorer] {
265 | get {
266 | return ((WindowFormRegionCollection)(Globals.ThisAddIn.GetFormRegions(explorer, typeof(WindowFormRegionCollection))));
267 | }
268 | }
269 |
270 | internal WindowFormRegionCollection this[Microsoft.Office.Interop.Outlook.Inspector inspector] {
271 | get {
272 | return ((WindowFormRegionCollection)(Globals.ThisAddIn.GetFormRegions(inspector, typeof(WindowFormRegionCollection))));
273 | }
274 | }
275 | }
276 |
277 | ///
278 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
279 | internal sealed partial class WindowFormRegionCollection : Microsoft.Office.Tools.Outlook.FormRegionCollectionBase {
280 |
281 | ///
282 | public WindowFormRegionCollection(System.Collections.Generic.IList list) :
283 | base(list) {
284 | }
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/ThisAddIn.Designer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/SaveAllTheHomework/ThisAddIn.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Xml.Linq;
6 | using Outlook = Microsoft.Office.Interop.Outlook;
7 | using Office = Microsoft.Office.Core;
8 |
9 | using SaveAllTheHomework.Ribbons;
10 | using SaveAllTheHomework.Source;
11 | using System.Collections;
12 |
13 | namespace SaveAllTheHomework
14 | {
15 | public partial class ThisAddIn
16 | {
17 |
18 | public HomeworkBot aHomeworkBot;
19 |
20 | private void ThisAddIn_Startup(object sender, System.EventArgs e)
21 | {
22 |
23 | }
24 |
25 | private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
26 | {
27 | // Note: Outlook no longer raises this event. If you have code that
28 | // must run when Outlook shuts down, see https://go.microsoft.com/fwlink/?LinkId=506785
29 | }
30 |
31 | protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
32 | {
33 | aHomeworkBot = new HomeworkBot();
34 |
35 | MainRibbon mainRibbon = new MainRibbon
36 | {
37 | aHomeworkBot = aHomeworkBot
38 | };
39 | return mainRibbon;
40 | }
41 |
42 |
43 | #region VSTO generated code
44 |
45 | ///
46 | /// Required method for Designer support - do not modify
47 | /// the contents of this method with the code editor.
48 | ///
49 | private void InternalStartup()
50 | {
51 | this.Startup += new System.EventHandler(ThisAddIn_Startup);
52 | this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
53 | }
54 |
55 | #endregion
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # 收作业智能姬
2 |
3 | ## 介绍
4 | 一个用于将当前 Outlook 文件夹下的所有邮件附件保存到本地的插件。
5 |
6 | 如想拿走这个奇怪的小插件,请将 [SaveAllTheHomework.Source.HomeworkBot](SaveAllTheHomework/Source/HomeworkBot.cs#L191) 以下的学号识别代码修改一下就好辣!
7 |
8 | ## 特性
9 | - 更加灵活的学号解析功能。
10 | - 会尝试从发件人地址解析 ```^(\d{13})@whu.edu.cn``` 。
11 | - 会尝试从邮件标题解析 ```(\d{13})``` 。
12 | - 会尝试从附件文件名解析 ```(\d{13})``` 。
13 | - 更加智能的附件保存功能。
14 | - 如有同一学号多次发送带附件的邮件,只会保存最新的一次邮件发送的附件。
15 | - 如遇某邮件携带多个附件,会将多个附件保存到以学号命名的子文件夹中。
16 |
17 | ## 使用
18 |
19 | ### 安装
20 |
21 | 1. 克隆或下载本仓库。
22 | 2. 使用 Visual Studio 打开本项目。
23 | 3. 编译并运行本项目。
24 |
25 | ### 使用
26 |
27 | 1. 在 Outlook 插件中将会看到本插件注册的一个按钮。
28 | 点击按钮即可将当前 Outlook 文件夹下的所有邮件附件保存到本地。
--------------------------------------------------------------------------------