├── .gitignore
├── RedditSkylines
├── CitizenInfo.cs
├── ModInfo.cs
├── Properties
│ └── AssemblyInfo.cs
├── RedditClient.csproj
├── TinyWeb.cs
├── Message.cs
├── Configuration.cs
├── RedditUpdater.cs
└── SimpleJSON.cs
├── README.md
├── LICENSE
├── Compatibility
├── Message.cs
├── v2
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ └── compat.v2.csproj
├── v3
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ └── compat.v3.csproj
├── v4
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ └── compat.v4.csproj
└── v5
│ ├── Properties
│ └── AssemblyInfo.cs
│ └── compat.v5.csproj
└── RedditClient.sln
/.gitignore:
--------------------------------------------------------------------------------
1 | *.suo
2 | [Rr]elease/
3 | [Dd]ebug/
4 |
--------------------------------------------------------------------------------
/RedditSkylines/CitizenInfo.cs:
--------------------------------------------------------------------------------
1 | namespace RedditClient
2 | {
3 | ///
4 | /// I would've used tuples but the mod would no longer load.
5 | ///
6 | internal class CitizenInfo
7 | {
8 | public string Name;
9 | public uint ID;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/RedditSkylines/ModInfo.cs:
--------------------------------------------------------------------------------
1 | using ICities;
2 |
3 | namespace RedditClient
4 | {
5 | public class ModInfo : IUserMod
6 | {
7 | public string Description
8 | {
9 | get { return "Show what's new on Reddit"; }
10 | }
11 |
12 | public string Name
13 | {
14 | get { return "Reddit for Chirpy"; }
15 | }
16 |
17 | public static int Version
18 | {
19 | get { return 9; }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Reddit for Chirpy (a Cities: Skylines mod)
2 | ### Info
3 |
4 | * [Configuration explained](https://github.com/mabako/reddit-for-city-skylines/wiki/Configuration)
5 |
6 | # Links
7 |
8 | * [on Steam Workshop](http://steamcommunity.com/sharedfiles/filedetails/?id=408705348)
9 | * [on /r/CitiesSkylines](https://www.reddit.com/r/CitiesSkylines/comments/2z87if/reddit_for_chirpy_view_whats_new_on_reddit_ingame/)
10 | * [on /r/CitiesSkylinesModding](https://www.reddit.com/r/CitiesSkylinesModding/comments/2z68bw/reddit_for_chirpy_wip_showerthoughts_on_your/)
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Marcus Bauer
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 |
--------------------------------------------------------------------------------
/Compatibility/Message.cs:
--------------------------------------------------------------------------------
1 | namespace RedditClient
2 | {
3 | public class Message : MessageBase
4 | {
5 | private string m_author;
6 | private string m_subreddit;
7 | private string m_text;
8 |
9 | public override uint GetSenderID()
10 | {
11 | return 0;
12 | }
13 |
14 | public override string GetSenderName()
15 | {
16 | return m_author;
17 | }
18 |
19 | public override string GetText()
20 | {
21 | return string.Format("{0} #{1}", m_text, m_subreddit);
22 | }
23 |
24 | ///
25 | /// We basically want to ensure the same messages aren't shown twice.
26 | ///
27 | ///
28 | ///
29 | public override bool IsSimilarMessage(MessageBase other)
30 | {
31 | return false;
32 | }
33 |
34 | public override void Serialize(ColossalFramework.IO.DataSerializer s)
35 | {
36 | s.WriteSharedString(m_author);
37 | s.WriteSharedString(m_subreddit);
38 | s.WriteSharedString(m_text);
39 | }
40 |
41 | public override void Deserialize(ColossalFramework.IO.DataSerializer s)
42 | {
43 | m_author = s.ReadSharedString();
44 | m_subreddit = s.ReadSharedString();
45 | m_text = s.ReadSharedString();
46 | }
47 |
48 | public override void AfterDeserialize(ColossalFramework.IO.DataSerializer s)
49 | {
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/RedditSkylines/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // Allgemeine Informationen über eine Assembly werden über die folgenden
5 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
6 | // die mit einer Assembly verknüpft sind.
7 | [assembly: AssemblyTitle("Reddit for Chirpy")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("Reddit for Chirpy")]
12 | [assembly: AssemblyCopyright("Copyright © 2015 Marcus Bauer")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
17 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
18 | // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
19 | [assembly: ComVisible(false)]
20 |
21 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
22 | [assembly: Guid("e678dd7d-2a04-4085-bf21-d5dc9d4fbbf2")]
23 |
24 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
25 | //
26 | // Hauptversion
27 | // Nebenversion
28 | // Buildnummer
29 | // Revision
30 | //
31 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
32 | // übernehmen, indem Sie "*" eingeben:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("7.0.0.0")]
35 | [assembly: AssemblyFileVersion("7.0.0.0")]
36 |
--------------------------------------------------------------------------------
/Compatibility/v2/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // Allgemeine Informationen über eine Assembly werden über die folgenden
5 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
6 | // die mit einer Assembly verknüpft sind.
7 | [assembly: AssemblyTitle("Reddit for Chirpy")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("Reddit for Chirpy")]
12 | [assembly: AssemblyCopyright("Copyright © 2015 Marcus Bauer")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
17 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
18 | // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
19 | [assembly: ComVisible(false)]
20 |
21 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
22 | [assembly: Guid("e678dd7d-2a04-4085-bf21-d5dc9d4fbbf2")]
23 |
24 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
25 | //
26 | // Hauptversion
27 | // Nebenversion
28 | // Buildnummer
29 | // Revision
30 | //
31 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
32 | // übernehmen, indem Sie "*" eingeben:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("2.0.0.0")]
35 | [assembly: AssemblyFileVersion("2.0.0.0")]
36 |
--------------------------------------------------------------------------------
/Compatibility/v3/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // Allgemeine Informationen über eine Assembly werden über die folgenden
5 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
6 | // die mit einer Assembly verknüpft sind.
7 | [assembly: AssemblyTitle("Reddit for Chirpy")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("Reddit for Chirpy")]
12 | [assembly: AssemblyCopyright("Copyright © 2015 Marcus Bauer")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
17 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
18 | // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
19 | [assembly: ComVisible(false)]
20 |
21 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
22 | [assembly: Guid("e678dd7d-2a04-4085-bf21-d5dc9d4fbbf2")]
23 |
24 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
25 | //
26 | // Hauptversion
27 | // Nebenversion
28 | // Buildnummer
29 | // Revision
30 | //
31 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
32 | // übernehmen, indem Sie "*" eingeben:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("3.0.0.0")]
35 | [assembly: AssemblyFileVersion("3.0.0.0")]
36 |
--------------------------------------------------------------------------------
/Compatibility/v4/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // Allgemeine Informationen über eine Assembly werden über die folgenden
5 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
6 | // die mit einer Assembly verknüpft sind.
7 | [assembly: AssemblyTitle("Reddit for Chirpy")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("Reddit for Chirpy")]
12 | [assembly: AssemblyCopyright("Copyright © 2015 Marcus Bauer")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
17 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
18 | // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
19 | [assembly: ComVisible(false)]
20 |
21 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
22 | [assembly: Guid("e678dd7d-2a04-4085-bf21-d5dc9d4fbbf2")]
23 |
24 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
25 | //
26 | // Hauptversion
27 | // Nebenversion
28 | // Buildnummer
29 | // Revision
30 | //
31 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
32 | // übernehmen, indem Sie "*" eingeben:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("4.0.0.0")]
35 | [assembly: AssemblyFileVersion("4.0.0.0")]
36 |
--------------------------------------------------------------------------------
/Compatibility/v5/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // Allgemeine Informationen über eine Assembly werden über die folgenden
5 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
6 | // die mit einer Assembly verknüpft sind.
7 | [assembly: AssemblyTitle("Reddit for Chirpy")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("Reddit for Chirpy")]
12 | [assembly: AssemblyCopyright("Copyright © 2015 Marcus Bauer")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
17 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
18 | // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
19 | [assembly: ComVisible(false)]
20 |
21 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
22 | [assembly: Guid("e678dd7d-2a04-4085-bf21-d5dc9d4fbbf2")]
23 |
24 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
25 | //
26 | // Hauptversion
27 | // Nebenversion
28 | // Buildnummer
29 | // Revision
30 | //
31 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
32 | // übernehmen, indem Sie "*" eingeben:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("5.0.0.0")]
35 | [assembly: AssemblyFileVersion("5.0.0.0")]
36 |
--------------------------------------------------------------------------------
/Compatibility/v2/compat.v2.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {2811E498-8EC2-4B04-971A-96AEFBA34CF7}
8 | Library
9 | Properties
10 | RedditClient
11 | reddit
12 | v3.5
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\Assembly-CSharp.dll
36 |
37 |
38 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\ColossalManaged.dll
39 |
40 |
41 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\ICities.dll
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | set CS_MOD_FOLDER=%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\
56 | set PROJECT_MOD_FOLDER=%25CS_MOD_FOLDER%25\$(SolutionName)\
57 | if not exist "%25PROJECT_MOD_FOLDER%25" mkdir "%25PROJECT_MOD_FOLDER%25"
58 | copy /y "$(TargetPath)" "%25PROJECT_MOD_FOLDER%25\$(ProjectName).dll"
59 |
60 |
67 |
--------------------------------------------------------------------------------
/Compatibility/v3/compat.v3.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {2811E498-8EC2-4B04-971A-96AEFBA34C3B}
8 | Library
9 | Properties
10 | RedditClient
11 | reddit
12 | v3.5
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\Assembly-CSharp.dll
36 |
37 |
38 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\ColossalManaged.dll
39 |
40 |
41 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\ICities.dll
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | set CS_MOD_FOLDER=%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\
56 | set PROJECT_MOD_FOLDER=%25CS_MOD_FOLDER%25\$(SolutionName)\
57 | if not exist "%25PROJECT_MOD_FOLDER%25" mkdir "%25PROJECT_MOD_FOLDER%25"
58 | copy /y "$(TargetPath)" "%25PROJECT_MOD_FOLDER%25\$(ProjectName).dll"
59 |
60 |
67 |
--------------------------------------------------------------------------------
/Compatibility/v4/compat.v4.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {2811E498-8EC2-4B04-971A-96AEFBA34C37}
8 | Library
9 | Properties
10 | RedditClient
11 | reddit
12 | v3.5
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\Assembly-CSharp.dll
36 |
37 |
38 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\ColossalManaged.dll
39 |
40 |
41 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\ICities.dll
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | set CS_MOD_FOLDER=%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\
56 | set PROJECT_MOD_FOLDER=%25CS_MOD_FOLDER%25\$(SolutionName)\
57 | if not exist "%25PROJECT_MOD_FOLDER%25" mkdir "%25PROJECT_MOD_FOLDER%25"
58 | copy /y "$(TargetPath)" "%25PROJECT_MOD_FOLDER%25\$(ProjectName).dll"
59 |
60 |
67 |
--------------------------------------------------------------------------------
/Compatibility/v5/compat.v5.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {2811E498-8FC2-4B04-971A-96AEFBA34C37}
8 | Library
9 | Properties
10 | RedditClient
11 | reddit
12 | v3.5
13 | 512
14 |
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\Assembly-CSharp.dll
36 |
37 |
38 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\ColossalManaged.dll
39 |
40 |
41 | C:\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\ICities.dll
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | set CS_MOD_FOLDER=%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\
56 | set PROJECT_MOD_FOLDER=%25CS_MOD_FOLDER%25\$(SolutionName)\
57 | if not exist "%25PROJECT_MOD_FOLDER%25" mkdir "%25PROJECT_MOD_FOLDER%25"
58 | copy /y "$(TargetPath)" "%25PROJECT_MOD_FOLDER%25\$(ProjectName).dll"
59 |
60 |
67 |
--------------------------------------------------------------------------------
/RedditClient.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.31101.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedditClient", "RedditSkylines\RedditClient.csproj", "{A28394F0-1ED9-4153-B6EB-274D290B5E38}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compatibility", "Compatibility", "{3F498AAF-364D-42E0-8786-347A263FF904}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "compat.v2", "Compatibility\v2\compat.v2.csproj", "{2811E498-8EC2-4B04-971A-96AEFBA34CF7}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "compat.v3", "Compatibility\v3\compat.v3.csproj", "{2811E498-8EC2-4B04-971A-96AEFBA34C3B}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "compat.v4", "Compatibility\v4\compat.v4.csproj", "{2811E498-8EC2-4B04-971A-96AEFBA34C37}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "compat.v5", "Compatibility\v5\compat.v5.csproj", "{2811E498-8FC2-4B04-971A-96AEFBA34C37}"
17 | EndProject
18 | Global
19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
20 | Debug|Any CPU = Debug|Any CPU
21 | Release|Any CPU = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {A28394F0-1ED9-4153-B6EB-274D290B5E38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {A28394F0-1ED9-4153-B6EB-274D290B5E38}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {A28394F0-1ED9-4153-B6EB-274D290B5E38}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {A28394F0-1ED9-4153-B6EB-274D290B5E38}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {2811E498-8EC2-4B04-971A-96AEFBA34CF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {2811E498-8EC2-4B04-971A-96AEFBA34CF7}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {2811E498-8EC2-4B04-971A-96AEFBA34CF7}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {2811E498-8EC2-4B04-971A-96AEFBA34CF7}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {2811E498-8EC2-4B04-971A-96AEFBA34C3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {2811E498-8EC2-4B04-971A-96AEFBA34C3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {2811E498-8EC2-4B04-971A-96AEFBA34C3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {2811E498-8EC2-4B04-971A-96AEFBA34C3B}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {2811E498-8EC2-4B04-971A-96AEFBA34C37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {2811E498-8EC2-4B04-971A-96AEFBA34C37}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {2811E498-8EC2-4B04-971A-96AEFBA34C37}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {2811E498-8EC2-4B04-971A-96AEFBA34C37}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {2811E498-8FC2-4B04-971A-96AEFBA34C37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {2811E498-8FC2-4B04-971A-96AEFBA34C37}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {2811E498-8FC2-4B04-971A-96AEFBA34C37}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {2811E498-8FC2-4B04-971A-96AEFBA34C37}.Release|Any CPU.Build.0 = Release|Any CPU
44 | EndGlobalSection
45 | GlobalSection(SolutionProperties) = preSolution
46 | HideSolutionNode = FALSE
47 | EndGlobalSection
48 | GlobalSection(NestedProjects) = preSolution
49 | {2811E498-8EC2-4B04-971A-96AEFBA34CF7} = {3F498AAF-364D-42E0-8786-347A263FF904}
50 | {2811E498-8EC2-4B04-971A-96AEFBA34C3B} = {3F498AAF-364D-42E0-8786-347A263FF904}
51 | {2811E498-8EC2-4B04-971A-96AEFBA34C37} = {3F498AAF-364D-42E0-8786-347A263FF904}
52 | {2811E498-8FC2-4B04-971A-96AEFBA34C37} = {3F498AAF-364D-42E0-8786-347A263FF904}
53 | EndGlobalSection
54 | EndGlobal
55 |
--------------------------------------------------------------------------------
/RedditSkylines/RedditClient.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {A28394F0-1ED9-4153-B6EB-274D290B5E38}
8 | Library
9 | Properties
10 | RedditClient
11 | reddit
12 | v3.5
13 | 512
14 |
15 |
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 | false
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 | false
35 |
36 |
37 |
38 | ..\..\..\..\..\..\..\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\Assembly-CSharp.dll
39 |
40 |
41 | ..\..\..\..\..\..\..\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\ColossalManaged.dll
42 |
43 |
44 | ..\..\..\..\..\..\..\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\ICities.dll
45 |
46 |
47 |
48 |
49 | ..\..\..\..\..\..\..\Program Files (x86)\Steam\SteamApps\common\Cities_Skylines\Cities_Data\Managed\UnityEngine.dll
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | set CS_MOD_FOLDER=%25LOCALAPPDATA%25\Colossal Order\Cities_Skylines\Addons\Mods\
65 | set PROJECT_MOD_FOLDER=%25CS_MOD_FOLDER%25\$(SolutionName)\
66 | if not exist "%25PROJECT_MOD_FOLDER%25" mkdir "%25PROJECT_MOD_FOLDER%25"
67 | copy /y "$(TargetPath)" "%25PROJECT_MOD_FOLDER%25"
68 | copy /y "$(SolutionDir)\LICENSE" "%25PROJECT_MOD_FOLDER%25"
69 |
70 |
77 |
--------------------------------------------------------------------------------
/RedditSkylines/TinyWeb.cs:
--------------------------------------------------------------------------------
1 | using SimpleJson;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Net;
6 |
7 | namespace RedditClient
8 | {
9 | internal class TinyWeb
10 | {
11 | private const string BASE_URL = "http://www.reddit.com{0}.json?limit={1}";
12 |
13 | private const string ANNOUNCEMENT_URL = "http://mabako.net/reddit-for-city-skylines/v{0}.txt";
14 |
15 | public static IEnumerable FindLastPosts(string subreddit)
16 | {
17 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(BASE_URL, subreddit, RedditUpdater.MAX_REDDIT_POSTS_PER_SUBREDDIT));
18 | request.Method = WebRequestMethods.Http.Get;
19 | request.Accept = "text/json";
20 |
21 | using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
22 | {
23 | if (response.StatusCode != HttpStatusCode.OK)
24 | return null;
25 |
26 | using (var sr = new StreamReader(response.GetResponseStream()))
27 | {
28 | string str = sr.ReadToEnd();
29 |
30 | JsonObject root = (JsonObject)SimpleJson.SimpleJson.DeserializeObject(str);
31 | JsonObject rootData = (JsonObject)root["data"];
32 | JsonArray rootChildren = (JsonArray)rootData["children"];
33 |
34 | var list = new List();
35 | foreach (object obj in rootChildren)
36 | {
37 | JsonObject child = (JsonObject)obj;
38 | JsonObject data = (JsonObject)child["data"];
39 |
40 | var post = createPost(data);
41 | if (post != null)
42 | list.Add(post);
43 | }
44 | return list;
45 | }
46 | }
47 | }
48 |
49 | public static string GetAnnouncement()
50 | {
51 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(ANNOUNCEMENT_URL, ModInfo.Version));
52 | request.Method = WebRequestMethods.Http.Get;
53 |
54 | using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
55 | {
56 | if (response.StatusCode != HttpStatusCode.OK)
57 | return null;
58 |
59 | using (var sr = new StreamReader(response.GetResponseStream()))
60 | {
61 | return sr.ReadLine();
62 | }
63 | }
64 | }
65 |
66 | private static RedditPost createPost(JsonObject data)
67 | {
68 | // Any karma at all?
69 | var karma = data["score"];
70 | if (karma is Int32)
71 | if ((Int32)karma <= 0)
72 | return null;
73 |
74 | // Sticky post?
75 | var sticky = data["stickied"];
76 | if (sticky is Boolean)
77 | if ((Boolean)sticky == true)
78 | return null;
79 |
80 | // create post object
81 | var post = new RedditPost { id = data["id"].ToString(), title = data["title"].ToString(), author = data["author"].ToString(), subreddit = data["subreddit"].ToString() };
82 |
83 | // does it have a flair?
84 | var flair = data["link_flair_text"];
85 | if (flair != null)
86 | {
87 | var flairStr = flair.ToString();
88 | if (flairStr.Equals("meta", StringComparison.InvariantCultureIgnoreCase))
89 | return null;
90 |
91 | post.title += " #" + flairStr.Replace(" ", "");
92 | }
93 |
94 | return post;
95 | }
96 | }
97 |
98 | internal class RedditPost
99 | {
100 | internal string title;
101 | internal string author;
102 | internal string id;
103 | internal string subreddit;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/RedditSkylines/Message.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace RedditClient
4 | {
5 | public class Message : MessageBase
6 | {
7 | private string m_author;
8 | private uint m_citizenId;
9 | private string m_subreddit;
10 | private string m_text;
11 |
12 | [NonSerialized]
13 | private string m_postId;
14 |
15 | public Message(string author, string subreddit, string text, uint citizenId, string postId)
16 | {
17 | m_author = author;
18 | m_subreddit = subreddit;
19 | m_text = text;
20 | m_citizenId = citizenId;
21 | m_postId = postId;
22 |
23 | if (Configuration.Hashtags > 0)
24 | HashtagThis();
25 | }
26 |
27 | private void HashtagThis()
28 | {
29 | var split = m_text.Split(' ');
30 |
31 | int desiredHashtags = split.Length / 4;
32 | int hashtags = m_text.Length - m_text.Replace("#", "").Length;
33 | if (hashtags >= desiredHashtags)
34 | return;
35 |
36 | // Get the longest, non-hashtagged word
37 | string longestWord = "";
38 | foreach (string str in split)
39 | {
40 | if (!str.StartsWith("#"))
41 | {
42 | int length = str.Length;
43 | if (length == 0)
44 | continue;
45 |
46 | if (!Char.IsLetter(str[0]))
47 | continue;
48 |
49 | // UPPERCASE WORDS ARE MORE IMPORTANT
50 | if (Char.IsUpper(str[0]))
51 | length += 2;
52 |
53 | // random bonus factor
54 | length += new Random().Next(2);
55 |
56 | if (length > longestWord.Length)
57 | {
58 | longestWord = str;
59 | }
60 | }
61 | }
62 |
63 | if (longestWord == "")
64 | return;
65 |
66 | for (int i = 0; i < split.Length; ++i)
67 | {
68 | if (longestWord.Equals(split[i], StringComparison.InvariantCultureIgnoreCase))
69 | {
70 | split[i] = "#" + split[i];
71 | }
72 | }
73 |
74 | m_text = string.Join(" ", split);
75 | HashtagThis();
76 | }
77 |
78 | public override uint GetSenderID()
79 | {
80 | return m_citizenId;
81 | }
82 |
83 | public override string GetSenderName()
84 | {
85 | return m_author;
86 | }
87 |
88 | public override string GetText()
89 | {
90 | return string.Format("{0} #{1}", m_text, m_subreddit);
91 | }
92 |
93 | ///
94 | /// We basically want to ensure the same messages aren't shown twice.
95 | ///
96 | ///
97 | ///
98 | public override bool IsSimilarMessage(MessageBase other)
99 | {
100 | var m = other as Message;
101 | return m != null && ((m.m_author == m_author && m.m_subreddit == m_subreddit) || m.m_text.Replace("#", "") == m_text.Replace("#", ""));
102 | }
103 |
104 | public override void Serialize(ColossalFramework.IO.DataSerializer s)
105 | {
106 | s.WriteSharedString(m_author);
107 | s.WriteSharedString(m_subreddit);
108 | s.WriteSharedString(m_text);
109 | s.WriteUInt32(m_citizenId);
110 | }
111 |
112 | public override void Deserialize(ColossalFramework.IO.DataSerializer s)
113 | {
114 | m_author = s.ReadSharedString();
115 | m_subreddit = s.ReadSharedString();
116 | m_text = s.ReadSharedString();
117 | m_citizenId = s.ReadUInt32();
118 | }
119 |
120 | public override void AfterDeserialize(ColossalFramework.IO.DataSerializer s)
121 | {
122 | }
123 |
124 | public string GetPostID()
125 | {
126 | return m_postId;
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/RedditSkylines/Configuration.cs:
--------------------------------------------------------------------------------
1 | using ColossalFramework.IO;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Text.RegularExpressions;
6 |
7 | namespace RedditClient
8 | {
9 | internal class Configuration
10 | {
11 | private const string TIMER_KEY = "updateFrequency";
12 | private const string FILTER_MESSAGES_KEY = "filterMessages";
13 | private const string LAST_ANNOUNCEMENT = "lastAnnouncementId";
14 | private const string ASSOCIATION_MODE = "associationMode";
15 | private const string HASHTAG_MODE = "hashtags";
16 | private const string CLICK_BEHAVIOUR = "clickBehaviour";
17 |
18 | public static List Subreddits;
19 | public static int TimerInSeconds = 300;
20 | public static int AssociationMode = 0;
21 | public static int Hashtags = 1;
22 | public static int ClickBehaviour = 0;
23 |
24 | public static int FilterMessages = 0;
25 | public static int LastAnnouncement = 0;
26 |
27 | private static string ConfigPath
28 | {
29 | get
30 | {
31 | // base it on the path Cities: Skylines uses
32 | string path = string.Format("{0}/{1}/", DataLocation.localApplicationData, "ModConfig");
33 | if (!Directory.Exists(path))
34 | Directory.CreateDirectory(path);
35 |
36 | path += "reddit-for-chirpy.txt";
37 |
38 | return path;
39 | }
40 | }
41 |
42 | ///
43 | /// Attempts to load the configuration. If it fails, it'll load the defaults
44 | ///
45 | internal static void Load()
46 | {
47 | try
48 | {
49 | string[] configLines = File.ReadAllLines(ConfigPath);
50 | Regex r = new Regex("^[a-zA-Z0-9/]*$");
51 |
52 | Subreddits = new List();
53 | bool requestSave = false;
54 |
55 | for (int i = 0; i < configLines.Length; ++i)
56 | {
57 | // Remove unnecessary spaces
58 | var line = configLines[i].Trim();
59 |
60 | // Comment lines
61 | if (line.StartsWith("#") || line.StartsWith(";"))
62 | continue;
63 |
64 | // Config options
65 | if (line.StartsWith(TIMER_KEY + "="))
66 | {
67 | var time = line.Substring(TIMER_KEY.Length + 1);
68 |
69 | int newTimer = -1;
70 | if (Int32.TryParse(time, out newTimer) && newTimer >= 10)
71 | {
72 | TimerInSeconds = newTimer;
73 | }
74 | }
75 | else if (line.StartsWith(ASSOCIATION_MODE + "="))
76 | {
77 | var sound = line.Substring(ASSOCIATION_MODE.Length + 1);
78 |
79 | int mode = -1;
80 | if (Int32.TryParse(sound, out mode) && (mode >= 0 || mode <= 2))
81 | {
82 | AssociationMode = mode;
83 | }
84 | }
85 | else if (line.StartsWith(HASHTAG_MODE + "="))
86 | {
87 | var sound = line.Substring(HASHTAG_MODE.Length + 1);
88 |
89 | int mode = -1;
90 | if (Int32.TryParse(sound, out mode) && (mode >= 0 || mode <= 1))
91 | {
92 | Hashtags = mode;
93 | }
94 | }
95 | else if (line.StartsWith(FILTER_MESSAGES_KEY + "="))
96 | {
97 | var filter = line.Substring(FILTER_MESSAGES_KEY.Length + 1);
98 |
99 | int newVal = -1;
100 | if (Int32.TryParse(filter, out newVal) && (newVal >= 0 || newVal <= 2))
101 | {
102 | FilterMessages = newVal;
103 | }
104 | }
105 | else if (line.StartsWith(LAST_ANNOUNCEMENT + "="))
106 | {
107 | var announcement = line.Substring(LAST_ANNOUNCEMENT.Length + 1);
108 |
109 | int newVal = 0;
110 | if (Int32.TryParse(announcement, out newVal))
111 | {
112 | LastAnnouncement = newVal;
113 | }
114 | }
115 | else if (line.StartsWith(CLICK_BEHAVIOUR + "="))
116 | {
117 | var clickb = line.Substring(CLICK_BEHAVIOUR.Length + 1);
118 |
119 | int newVal = 0;
120 | if (Int32.TryParse(clickb, out newVal) && (newVal >= 0 || newVal <= 3))
121 | {
122 | ClickBehaviour = newVal;
123 | }
124 | }
125 |
126 | // Just reddit names, presumably
127 | else if (line.Length > 1 && r.IsMatch(line))
128 | {
129 | if (line.IndexOf('/') == -1)
130 | {
131 | line = string.Format("/r/{0}/new", line);
132 | configLines[i] = line;
133 |
134 | requestSave = true;
135 | }
136 | Subreddits.Add(line);
137 | }
138 | }
139 |
140 | if (requestSave)
141 | {
142 | using (StreamWriter sw = new StreamWriter(ConfigPath))
143 | {
144 | foreach (string line in configLines)
145 | sw.WriteLine(line);
146 | }
147 | }
148 | }
149 | catch (Exception e)
150 | {
151 | DebugOutputPanel.AddMessage(ColossalFramework.Plugins.PluginManager.MessageType.Message, string.Format("Reddit: Config regenerated {0}: {1}", e.GetType().ToString(), e.Message));
152 |
153 | TimerInSeconds = 300;
154 | AssociationMode = 0;
155 | FilterMessages = 0;
156 | Subreddits = DefaultSubreddits;
157 | LastAnnouncement = 0;
158 | Hashtags = 1;
159 |
160 | SaveConfig(true);
161 | }
162 | }
163 |
164 | internal static void SaveConfig(bool defaultConfig)
165 | {
166 | using (StreamWriter sw = new StreamWriter(ConfigPath))
167 | {
168 | sw.WriteLine("# Reddit for Chirpy");
169 | sw.WriteLine("# https://github.com/mabako/reddit-for-city-skylines/wiki/Configuration");
170 | sw.WriteLine();
171 |
172 | sw.WriteLine("# How often should new messages be displayed?");
173 | sw.WriteLine("# Default: 300 (which is 5 minutes)");
174 | sw.WriteLine("{0}={1}", TIMER_KEY, TimerInSeconds);
175 | sw.WriteLine();
176 |
177 | sw.WriteLine("# How should names be handled?");
178 | sw.WriteLine("# Default: 0 (disabled)");
179 | sw.WriteLine("# Set this to '1' to use CIM names instead of reddit usernames.");
180 | sw.WriteLine("# Set this to '2' to permanently rename CIMs to reddit users.");
181 | sw.WriteLine("{0}={1}", ASSOCIATION_MODE, AssociationMode);
182 |
183 | sw.WriteLine();
184 | sw.WriteLine("# One subreddit per line");
185 |
186 | foreach (string subreddit in Subreddits)
187 | sw.WriteLine("{0}", subreddit);
188 |
189 | if (defaultConfig)
190 | {
191 | sw.WriteLine();
192 | sw.WriteLine("# Multireddit example (remove the '#' to use)");
193 | sw.WriteLine("# /user/ccaatt/m/chirps/new");
194 | }
195 |
196 | sw.WriteLine();
197 | sw.WriteLine("# Filters some or all chirps made by your citizen if enabled");
198 | sw.WriteLine("# Default: 0 (disabled)");
199 | sw.WriteLine("# Set this to '1' to hide useless chirps");
200 | sw.WriteLine("# Set this to '2' to hide all chirps");
201 | sw.WriteLine("# This may break mod compatibility.");
202 | sw.WriteLine("{0}={1}", FILTER_MESSAGES_KEY, FilterMessages);
203 |
204 | sw.WriteLine();
205 | sw.WriteLine("# Enable automated hashtags?");
206 | sw.WriteLine("{0}={1}", HASHTAG_MODE, Hashtags);
207 |
208 | sw.WriteLine();
209 | sw.WriteLine("# What should happen when you click on reddit chirps?");
210 | sw.WriteLine("# Default: 0 (open Steam Overlay)");
211 | sw.WriteLine("# Set this to '1' to copy to clipboard");
212 | sw.WriteLine("# Set this to '2' to open your system browser");
213 | sw.WriteLine("# Set this to '3' for nothing to happen");
214 | sw.WriteLine("{0}={1}", CLICK_BEHAVIOUR, ClickBehaviour);
215 |
216 | sw.WriteLine();
217 | sw.WriteLine("# INTERNAL CONFIG");
218 | sw.WriteLine("# Make sure to show announcements only once.");
219 | sw.WriteLine("{0}={1}", LAST_ANNOUNCEMENT, LastAnnouncement);
220 | }
221 | }
222 |
223 | private static List DefaultSubreddits
224 | {
225 | get
226 | {
227 | var s = new List();
228 | s.Add("/r/ShowerThoughts/rising/");
229 | s.Add("/r/CrazyIdeas/new/");
230 | s.Add("/r/ChirpIt/new/");
231 | return s;
232 | }
233 | }
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/RedditSkylines/RedditUpdater.cs:
--------------------------------------------------------------------------------
1 | using ColossalFramework;
2 | using ColossalFramework.Steamworks;
3 | using ColossalFramework.UI;
4 | using ICities;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Reflection;
8 | using System.Threading;
9 | using System.Timers;
10 | using UnityEngine;
11 |
12 | namespace RedditClient
13 | {
14 | public class RedditUpdater : ChirperExtensionBase
15 | {
16 | public const int MAX_REDDIT_POSTS_PER_SUBREDDIT = 5;
17 | public const int MAX_CACHED_REDDIT_POSTS_PER_SUBREDDIT = 50;
18 |
19 | private System.Timers.Timer timer = new System.Timers.Timer();
20 | private Dictionary> lastPostIds = new Dictionary>();
21 |
22 | private AudioClip messageSound = null;
23 | private bool checkedAnnouncement = false;
24 |
25 | private CitizenMessage lastCitizenMessage = null;
26 | public Message lastRedditMessage = null;
27 |
28 | private bool IsPaused
29 | {
30 | get
31 | {
32 | return SimulationManager.instance.SimulationPaused;
33 | }
34 | }
35 |
36 | public override void OnCreated(IChirper threading)
37 | {
38 | try
39 | {
40 | messageSound = Singleton.instance.m_NotificationSound;
41 |
42 | Configuration.Load();
43 | if (Configuration.Subreddits.Count >= 1)
44 | {
45 | DebugOutputPanel.AddMessage(ColossalFramework.Plugins.PluginManager.MessageType.Message, string.Format("Going to show a new message from one of {0} subreddits every {1} seconds (AssociationMode = {2})", Configuration.Subreddits.Count, Configuration.TimerInSeconds, Configuration.AssociationMode));
46 |
47 | foreach (string subreddit in Configuration.Subreddits)
48 | {
49 | if (!lastPostIds.ContainsKey(subreddit))
50 | lastPostIds.Add(subreddit, new Queue());
51 | }
52 |
53 | timer.AutoReset = true;
54 | timer.Elapsed += new ElapsedEventHandler((sender, e) => UpdateRedditPosts());
55 | timer.Interval = Configuration.TimerInSeconds * 1000;
56 | timer.Start();
57 | }
58 | else
59 | {
60 | DebugOutputPanel.AddMessage(ColossalFramework.Plugins.PluginManager.MessageType.Message, "No subreddits configured.");
61 | }
62 | }
63 | catch (Exception e)
64 | {
65 | DebugOutputPanel.AddMessage(ColossalFramework.Plugins.PluginManager.MessageType.Error, string.Format("[Reddit] {0}: {1}", e.GetType(), e.Message));
66 | }
67 | }
68 |
69 | public override void OnReleased()
70 | {
71 | timer.Stop();
72 | timer.Dispose();
73 |
74 | ChirpPanel cp = ChirpPanel.instance;
75 | if (cp != null)
76 | cp.m_NotificationSound = messageSound;
77 | }
78 |
79 | private void UpdateRedditPosts()
80 | {
81 | if (IsPaused)
82 | return;
83 |
84 | // Possibly important messages
85 | if (CheckAnnouncement())
86 | return;
87 |
88 | // Pick a subreddit at random
89 | string subreddit = Configuration.Subreddits[new System.Random().Next(Configuration.Subreddits.Count)];
90 |
91 | try
92 | {
93 | // Remove posts that are no longer checked against; plus some for possible deletions
94 | Queue lastPostId = lastPostIds[subreddit];
95 | while (lastPostId.Count > MAX_CACHED_REDDIT_POSTS_PER_SUBREDDIT)
96 | lastPostId.Dequeue();
97 |
98 | // Fetch a number of latest posts
99 | IEnumerable newestPosts = TinyWeb.FindLastPosts(subreddit);
100 | foreach (RedditPost newestPost in newestPosts)
101 | {
102 | // Find the first one we haven't shown yet
103 | if (!lastPostId.Contains(newestPost.id) && !ShouldFilterPost(newestPost))
104 | {
105 | var data = LookupOrRenameCitizenID(newestPost.author);
106 |
107 | AddMessage(new Message(data.Name, newestPost.subreddit, newestPost.title, data.ID, newestPost.id));
108 | lastPostIds[subreddit].Enqueue(newestPost.id);
109 | return;
110 | }
111 | }
112 | }
113 | catch (Exception)
114 | {
115 | // DebugOutputPanel.AddMessage(ColossalFramework.Plugins.PluginManager.MessageType.Message, string.Format("[Reddit {0}] {1}: {2}", subreddit, e.GetType().ToString(), e.Message));
116 | }
117 | }
118 |
119 | private CitizenInfo LookupOrRenameCitizenID(string name)
120 | {
121 | if (!string.IsNullOrEmpty(name))
122 | {
123 | if (Configuration.AssociationMode == 1)
124 | {
125 | // Use any citizen's name. Not necessarily consistent in and of itself, in that the same person may show up as multiple actual CIMs.
126 | uint id = MessageManager.instance.GetRandomResidentID();
127 | if (id != 0u)
128 | return new CitizenInfo { ID = id, Name = CitizenManager.instance.GetCitizenName(id) };
129 | }
130 | else if (Configuration.AssociationMode == 2)
131 | {
132 | // Overwrite any CIM's name by their reddit username.
133 | // To be fair: this was the more interesting part.
134 | try
135 | {
136 | // use the shared lock for this
137 | object L = GetPrivateVariable