├── .gitattributes
├── .gitignore
├── GitVersion.yml
├── HashBus.sln
├── HashBus.sln.DotSettings
├── LICENSE
├── README.md
├── nuget.config
└── src
├── HashBus.NServiceBusConfiguration
├── BusConfigurationExtensions.cs
└── HashBus.NServiceBusConfiguration.csproj
├── HashBus.Projector.MostHashtagged
├── App.cs
├── HashBus.Projector.MostHashtagged.csproj
├── MostHashtaggedProjection.cs
├── Program.cs
└── app.config
├── HashBus.Projector.MostMentioned
├── App.cs
├── HashBus.Projector.MostMentioned.csproj
├── MostMentionedProjection.cs
├── Program.cs
└── app.config
├── HashBus.Projector.MostRetweeted
├── App.cs
├── HashBus.Projector.MostRetweeted.csproj
├── MostRetweetedProjection.cs
├── Program.cs
└── app.config
├── HashBus.Projector.TopRetweeters
├── App.cs
├── HashBus.Projector.TopRetweeters.csproj
├── Program.cs
├── TopRetweetersProjection.cs
└── app.config
├── HashBus.Projector.TopTweeters
├── App.cs
├── HashBus.Projector.TopTweeters.csproj
├── Program.cs
├── TopTweetersProjection.cs
└── app.config
├── HashBus.Projector.TopTweetersRetweeters
├── App.cs
├── HashBus.Projector.TopTweetersRetweeters.csproj
├── Program.cs
├── TopTweetersRetweetersProjection.cs
└── app.config
├── HashBus.ReadModel.MongoDB
├── HashBus.ReadModel.MongoDB.csproj
└── MongoDBListRepository.cs
├── HashBus.ReadModel
├── HashBus.ReadModel.csproj
├── Hashtag.cs
├── IRepository.cs
├── Mention.cs
├── Retweet.cs
├── Retweetee.cs
├── Tweet.cs
└── TweetRetweet.cs
├── HashBus.Twitter.Analyzer.Commands
├── AnalyzeTweet.cs
├── HashBus.Twitter.Analyzer.Commands.csproj
├── Hashtag.cs
├── Tweet.cs
└── UserMention.cs
├── HashBus.Twitter.Analyzer.Events
├── HashBus.Twitter.Analyzer.Events.csproj
├── Hashtag.cs
├── Tweet.cs
├── TweetAnalyzed.cs
└── UserMention.cs
├── HashBus.Twitter.Analyzer
├── AnalyzeTweetHandler.cs
├── App.cs
├── HashBus.Twitter.Analyzer.csproj
├── Program.cs
├── TweetMapper.cs
└── app.config
├── HashBus.Twitter.BackFill
├── App.cs
├── HashBus.Twitter.BackFill.csproj
├── Program.cs
└── app.config
├── HashBus.Twitter.CatchUp.Commands
├── HashBus.Twitter.CatchUp.Commands.csproj
└── StartCatchUp.cs
├── HashBus.Twitter.CatchUp
├── App.cs
├── HashBus.Twitter.CatchUp.csproj
├── ITweetService.cs
├── Program.cs
├── StartCatchUpHandler.cs
├── TweetReceivedSaga.cs
├── TweetReceivedSagaData.cs
├── TweetReceivedSagaFinder.cs
├── TweetService.cs
└── app.config
├── HashBus.Twitter.Monitor.Events
├── HashBus.Twitter.Monitor.Events.csproj
└── TweetReceived.cs
├── HashBus.Twitter.Monitor.Simulator
├── App.cs
├── HashBus.Twitter.Monitor.Simulator.csproj
├── Program.cs
├── Simulation.cs
└── app.config
├── HashBus.Twitter.Monitor
├── App.cs
├── HashBus.Twitter.Monitor.csproj
├── Monitoring.cs
├── Program.cs
├── TweetMapper.cs
├── Writer.cs
└── app.config
├── HashBus.Viewer
├── App.cs
├── ColorTokenExtensions.cs
├── ConsoleHelper.cs
├── HashBus.Viewer.csproj
├── IRunAsync.cs
├── IService.cs
├── LeaderboardService.cs
├── LeaderboardView.cs
├── MostHashtaggedLeaderBoardViewFactory.cs
├── MostMentionedLeaderBoardViewFactory.cs
├── MostRetweetedLeaderBoardViewFactory.cs
├── Program.cs
├── TopRetweetersLeaderBoardViewFactory.cs
├── TopTweetersLeaderBoardViewFactory.cs
├── TopTweetersRetweetersLeaderBoardViewFactory.cs
└── app.config
└── HashBus.WebApi
├── App.cs
├── HashBus.WebApi.csproj
├── HashtagEntry.cs
├── IEntry.cs
├── IIgnoredHashtagsService.cs
├── IIgnoredUserNamesService.cs
├── IgnoredHashtagsService.cs
├── IgnoredUserNamesService.cs
├── Leaderboard.cs
├── MostHashtaggedModule.cs
├── MostMentionedModule.cs
├── MostRetweetedModule.cs
├── Program.cs
├── TopRetweetersModule.cs
├── TopTweetersModule.cs
├── TopTweetersRetweetersModule.cs
├── UserEntry.cs
└── app.config
/.gitattributes:
--------------------------------------------------------------------------------
1 | * -text
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | bin/
3 | obj/
4 | packages/
5 |
6 | *.suo
7 | *.user
8 |
--------------------------------------------------------------------------------
/GitVersion.yml:
--------------------------------------------------------------------------------
1 | assembly-versioning-scheme: Major
2 | next-version: 5
3 |
--------------------------------------------------------------------------------
/HashBus.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27130.2010
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Twitter.Monitor.Events", "src\HashBus.Twitter.Monitor.Events\HashBus.Twitter.Monitor.Events.csproj", "{5728DE02-C264-4697-B554-DDA5FAD10A47}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Twitter.Monitor.Simulator", "src\HashBus.Twitter.Monitor.Simulator\HashBus.Twitter.Monitor.Simulator.csproj", "{1BEFE50E-24CA-4CAA-8B2C-7F8CD1771CB5}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Twitter.Analyzer", "src\HashBus.Twitter.Analyzer\HashBus.Twitter.Analyzer.csproj", "{EC04FD05-F3FE-439A-8791-D85856E3726A}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Twitter.Monitor", "src\HashBus.Twitter.Monitor\HashBus.Twitter.Monitor.csproj", "{B1453AE4-B343-42F0-B6F6-E9016A62B3C2}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B34C7F0A-2903-47F2-A087-06D3136C9A1F}"
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B77C5EBC-7265-4E5E-B22A-5928FAFDC674}"
17 | ProjectSection(SolutionItems) = preProject
18 | .gitattributes = .gitattributes
19 | .gitignore = .gitignore
20 | GitVersion.yml = GitVersion.yml
21 | HashBus.sln.DotSettings = HashBus.sln.DotSettings
22 | LICENSE = LICENSE
23 | nuget.config = nuget.config
24 | README.md = README.md
25 | EndProjectSection
26 | EndProject
27 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Twitter.Analyzer.Events", "src\HashBus.Twitter.Analyzer.Events\HashBus.Twitter.Analyzer.Events.csproj", "{6991A5CF-25CB-4336-9F6D-2EB1E01EFB75}"
28 | EndProject
29 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Projector.MostMentioned", "src\HashBus.Projector.MostMentioned\HashBus.Projector.MostMentioned.csproj", "{718D4969-A6F7-4B0B-8FD2-DE13F23F5860}"
30 | EndProject
31 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.ReadModel", "src\HashBus.ReadModel\HashBus.ReadModel.csproj", "{1709F0A9-F797-44FD-94C0-18A0D75442F8}"
32 | EndProject
33 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.ReadModel.MongoDB", "src\HashBus.ReadModel.MongoDB\HashBus.ReadModel.MongoDB.csproj", "{34C86079-CCAB-4312-98FC-F64F4A97D3A4}"
34 | EndProject
35 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Projector.TopTweeters", "src\HashBus.Projector.TopTweeters\HashBus.Projector.TopTweeters.csproj", "{501F4158-CF10-4C25-AAA3-7BE827924926}"
36 | EndProject
37 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.WebApi", "src\HashBus.WebApi\HashBus.WebApi.csproj", "{4EC0F237-1CF1-4A0F-B849-E8B75A18CEEB}"
38 | EndProject
39 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.NServiceBusConfiguration", "src\HashBus.NServiceBusConfiguration\HashBus.NServiceBusConfiguration.csproj", "{F302F223-C0CD-46A6-8BB6-01F1213BDD58}"
40 | EndProject
41 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Twitter.CatchUp", "src\HashBus.Twitter.CatchUp\HashBus.Twitter.CatchUp.csproj", "{63B64572-B72C-4BCE-BB3D-C3F7635A6858}"
42 | EndProject
43 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Twitter.CatchUp.Commands", "src\HashBus.Twitter.CatchUp.Commands\HashBus.Twitter.CatchUp.Commands.csproj", "{89C81FA0-A06B-4FB9-A3FC-4DAB2A93AA2C}"
44 | EndProject
45 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Projector.TopTweetersRetweeters", "src\HashBus.Projector.TopTweetersRetweeters\HashBus.Projector.TopTweetersRetweeters.csproj", "{BAD64799-5E68-4EA5-B117-1F0B3582392B}"
46 | EndProject
47 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Projector.TopRetweeters", "src\HashBus.Projector.TopRetweeters\HashBus.Projector.TopRetweeters.csproj", "{82A07696-48E6-4EA7-9DF6-3422157E861F}"
48 | EndProject
49 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Projector.MostRetweeted", "src\HashBus.Projector.MostRetweeted\HashBus.Projector.MostRetweeted.csproj", "{C345A6AA-A562-447F-8A2B-63645B4FE56F}"
50 | EndProject
51 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Projector.MostHashtagged", "src\HashBus.Projector.MostHashtagged\HashBus.Projector.MostHashtagged.csproj", "{ADADC124-AF03-428D-ADD5-9CB2DF88E81C}"
52 | EndProject
53 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Twitter.BackFill", "src\HashBus.Twitter.BackFill\HashBus.Twitter.BackFill.csproj", "{F36FD7F7-0C41-44E3-89FE-AAF560BA69DC}"
54 | EndProject
55 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Twitter.Analyzer.Commands", "src\HashBus.Twitter.Analyzer.Commands\HashBus.Twitter.Analyzer.Commands.csproj", "{293F96F9-7E15-4969-B8ED-595D06FD7AA5}"
56 | EndProject
57 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HashBus.Viewer", "src\HashBus.Viewer\HashBus.Viewer.csproj", "{8E09CB51-03C5-413A-8570-1A121A01B0A4}"
58 | EndProject
59 | Global
60 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
61 | Debug|Any CPU = Debug|Any CPU
62 | Release|Any CPU = Release|Any CPU
63 | EndGlobalSection
64 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
65 | {5728DE02-C264-4697-B554-DDA5FAD10A47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
66 | {5728DE02-C264-4697-B554-DDA5FAD10A47}.Debug|Any CPU.Build.0 = Debug|Any CPU
67 | {5728DE02-C264-4697-B554-DDA5FAD10A47}.Release|Any CPU.ActiveCfg = Release|Any CPU
68 | {5728DE02-C264-4697-B554-DDA5FAD10A47}.Release|Any CPU.Build.0 = Release|Any CPU
69 | {1BEFE50E-24CA-4CAA-8B2C-7F8CD1771CB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
70 | {1BEFE50E-24CA-4CAA-8B2C-7F8CD1771CB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
71 | {1BEFE50E-24CA-4CAA-8B2C-7F8CD1771CB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
72 | {1BEFE50E-24CA-4CAA-8B2C-7F8CD1771CB5}.Release|Any CPU.Build.0 = Release|Any CPU
73 | {EC04FD05-F3FE-439A-8791-D85856E3726A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
74 | {EC04FD05-F3FE-439A-8791-D85856E3726A}.Debug|Any CPU.Build.0 = Debug|Any CPU
75 | {EC04FD05-F3FE-439A-8791-D85856E3726A}.Release|Any CPU.ActiveCfg = Release|Any CPU
76 | {EC04FD05-F3FE-439A-8791-D85856E3726A}.Release|Any CPU.Build.0 = Release|Any CPU
77 | {B1453AE4-B343-42F0-B6F6-E9016A62B3C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
78 | {B1453AE4-B343-42F0-B6F6-E9016A62B3C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
79 | {B1453AE4-B343-42F0-B6F6-E9016A62B3C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
80 | {B1453AE4-B343-42F0-B6F6-E9016A62B3C2}.Release|Any CPU.Build.0 = Release|Any CPU
81 | {6991A5CF-25CB-4336-9F6D-2EB1E01EFB75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
82 | {6991A5CF-25CB-4336-9F6D-2EB1E01EFB75}.Debug|Any CPU.Build.0 = Debug|Any CPU
83 | {6991A5CF-25CB-4336-9F6D-2EB1E01EFB75}.Release|Any CPU.ActiveCfg = Release|Any CPU
84 | {6991A5CF-25CB-4336-9F6D-2EB1E01EFB75}.Release|Any CPU.Build.0 = Release|Any CPU
85 | {718D4969-A6F7-4B0B-8FD2-DE13F23F5860}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
86 | {718D4969-A6F7-4B0B-8FD2-DE13F23F5860}.Debug|Any CPU.Build.0 = Debug|Any CPU
87 | {718D4969-A6F7-4B0B-8FD2-DE13F23F5860}.Release|Any CPU.ActiveCfg = Release|Any CPU
88 | {718D4969-A6F7-4B0B-8FD2-DE13F23F5860}.Release|Any CPU.Build.0 = Release|Any CPU
89 | {1709F0A9-F797-44FD-94C0-18A0D75442F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
90 | {1709F0A9-F797-44FD-94C0-18A0D75442F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
91 | {1709F0A9-F797-44FD-94C0-18A0D75442F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
92 | {1709F0A9-F797-44FD-94C0-18A0D75442F8}.Release|Any CPU.Build.0 = Release|Any CPU
93 | {34C86079-CCAB-4312-98FC-F64F4A97D3A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
94 | {34C86079-CCAB-4312-98FC-F64F4A97D3A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
95 | {34C86079-CCAB-4312-98FC-F64F4A97D3A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
96 | {34C86079-CCAB-4312-98FC-F64F4A97D3A4}.Release|Any CPU.Build.0 = Release|Any CPU
97 | {501F4158-CF10-4C25-AAA3-7BE827924926}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
98 | {501F4158-CF10-4C25-AAA3-7BE827924926}.Debug|Any CPU.Build.0 = Debug|Any CPU
99 | {501F4158-CF10-4C25-AAA3-7BE827924926}.Release|Any CPU.ActiveCfg = Release|Any CPU
100 | {501F4158-CF10-4C25-AAA3-7BE827924926}.Release|Any CPU.Build.0 = Release|Any CPU
101 | {4EC0F237-1CF1-4A0F-B849-E8B75A18CEEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
102 | {4EC0F237-1CF1-4A0F-B849-E8B75A18CEEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
103 | {4EC0F237-1CF1-4A0F-B849-E8B75A18CEEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
104 | {4EC0F237-1CF1-4A0F-B849-E8B75A18CEEB}.Release|Any CPU.Build.0 = Release|Any CPU
105 | {F302F223-C0CD-46A6-8BB6-01F1213BDD58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
106 | {F302F223-C0CD-46A6-8BB6-01F1213BDD58}.Debug|Any CPU.Build.0 = Debug|Any CPU
107 | {F302F223-C0CD-46A6-8BB6-01F1213BDD58}.Release|Any CPU.ActiveCfg = Release|Any CPU
108 | {F302F223-C0CD-46A6-8BB6-01F1213BDD58}.Release|Any CPU.Build.0 = Release|Any CPU
109 | {63B64572-B72C-4BCE-BB3D-C3F7635A6858}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
110 | {63B64572-B72C-4BCE-BB3D-C3F7635A6858}.Debug|Any CPU.Build.0 = Debug|Any CPU
111 | {63B64572-B72C-4BCE-BB3D-C3F7635A6858}.Release|Any CPU.ActiveCfg = Release|Any CPU
112 | {63B64572-B72C-4BCE-BB3D-C3F7635A6858}.Release|Any CPU.Build.0 = Release|Any CPU
113 | {89C81FA0-A06B-4FB9-A3FC-4DAB2A93AA2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
114 | {89C81FA0-A06B-4FB9-A3FC-4DAB2A93AA2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
115 | {89C81FA0-A06B-4FB9-A3FC-4DAB2A93AA2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
116 | {89C81FA0-A06B-4FB9-A3FC-4DAB2A93AA2C}.Release|Any CPU.Build.0 = Release|Any CPU
117 | {BAD64799-5E68-4EA5-B117-1F0B3582392B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
118 | {BAD64799-5E68-4EA5-B117-1F0B3582392B}.Debug|Any CPU.Build.0 = Debug|Any CPU
119 | {BAD64799-5E68-4EA5-B117-1F0B3582392B}.Release|Any CPU.ActiveCfg = Release|Any CPU
120 | {BAD64799-5E68-4EA5-B117-1F0B3582392B}.Release|Any CPU.Build.0 = Release|Any CPU
121 | {82A07696-48E6-4EA7-9DF6-3422157E861F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
122 | {82A07696-48E6-4EA7-9DF6-3422157E861F}.Debug|Any CPU.Build.0 = Debug|Any CPU
123 | {82A07696-48E6-4EA7-9DF6-3422157E861F}.Release|Any CPU.ActiveCfg = Release|Any CPU
124 | {82A07696-48E6-4EA7-9DF6-3422157E861F}.Release|Any CPU.Build.0 = Release|Any CPU
125 | {C345A6AA-A562-447F-8A2B-63645B4FE56F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
126 | {C345A6AA-A562-447F-8A2B-63645B4FE56F}.Debug|Any CPU.Build.0 = Debug|Any CPU
127 | {C345A6AA-A562-447F-8A2B-63645B4FE56F}.Release|Any CPU.ActiveCfg = Release|Any CPU
128 | {C345A6AA-A562-447F-8A2B-63645B4FE56F}.Release|Any CPU.Build.0 = Release|Any CPU
129 | {ADADC124-AF03-428D-ADD5-9CB2DF88E81C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
130 | {ADADC124-AF03-428D-ADD5-9CB2DF88E81C}.Debug|Any CPU.Build.0 = Debug|Any CPU
131 | {ADADC124-AF03-428D-ADD5-9CB2DF88E81C}.Release|Any CPU.ActiveCfg = Release|Any CPU
132 | {ADADC124-AF03-428D-ADD5-9CB2DF88E81C}.Release|Any CPU.Build.0 = Release|Any CPU
133 | {F36FD7F7-0C41-44E3-89FE-AAF560BA69DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
134 | {F36FD7F7-0C41-44E3-89FE-AAF560BA69DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
135 | {F36FD7F7-0C41-44E3-89FE-AAF560BA69DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
136 | {F36FD7F7-0C41-44E3-89FE-AAF560BA69DC}.Release|Any CPU.Build.0 = Release|Any CPU
137 | {293F96F9-7E15-4969-B8ED-595D06FD7AA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
138 | {293F96F9-7E15-4969-B8ED-595D06FD7AA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
139 | {293F96F9-7E15-4969-B8ED-595D06FD7AA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
140 | {293F96F9-7E15-4969-B8ED-595D06FD7AA5}.Release|Any CPU.Build.0 = Release|Any CPU
141 | {8E09CB51-03C5-413A-8570-1A121A01B0A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
142 | {8E09CB51-03C5-413A-8570-1A121A01B0A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
143 | {8E09CB51-03C5-413A-8570-1A121A01B0A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
144 | {8E09CB51-03C5-413A-8570-1A121A01B0A4}.Release|Any CPU.Build.0 = Release|Any CPU
145 | EndGlobalSection
146 | GlobalSection(SolutionProperties) = preSolution
147 | HideSolutionNode = FALSE
148 | EndGlobalSection
149 | GlobalSection(NestedProjects) = preSolution
150 | {5728DE02-C264-4697-B554-DDA5FAD10A47} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
151 | {1BEFE50E-24CA-4CAA-8B2C-7F8CD1771CB5} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
152 | {EC04FD05-F3FE-439A-8791-D85856E3726A} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
153 | {B1453AE4-B343-42F0-B6F6-E9016A62B3C2} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
154 | {6991A5CF-25CB-4336-9F6D-2EB1E01EFB75} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
155 | {718D4969-A6F7-4B0B-8FD2-DE13F23F5860} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
156 | {1709F0A9-F797-44FD-94C0-18A0D75442F8} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
157 | {34C86079-CCAB-4312-98FC-F64F4A97D3A4} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
158 | {501F4158-CF10-4C25-AAA3-7BE827924926} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
159 | {4EC0F237-1CF1-4A0F-B849-E8B75A18CEEB} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
160 | {F302F223-C0CD-46A6-8BB6-01F1213BDD58} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
161 | {63B64572-B72C-4BCE-BB3D-C3F7635A6858} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
162 | {89C81FA0-A06B-4FB9-A3FC-4DAB2A93AA2C} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
163 | {BAD64799-5E68-4EA5-B117-1F0B3582392B} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
164 | {82A07696-48E6-4EA7-9DF6-3422157E861F} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
165 | {C345A6AA-A562-447F-8A2B-63645B4FE56F} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
166 | {ADADC124-AF03-428D-ADD5-9CB2DF88E81C} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
167 | {F36FD7F7-0C41-44E3-89FE-AAF560BA69DC} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
168 | {293F96F9-7E15-4969-B8ED-595D06FD7AA5} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
169 | {8E09CB51-03C5-413A-8570-1A121A01B0A4} = {B34C7F0A-2903-47F2-A087-06D3136C9A1F}
170 | EndGlobalSection
171 | GlobalSection(ExtensibilityGlobals) = postSolution
172 | SolutionGuid = {3F3244FD-DDC3-4CF2-8E66-1B4EDD8109D6}
173 | EndGlobalSection
174 | EndGlobal
175 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 NServiceBus Ltd.
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Prerequisites
2 |
3 | * [MongoDB](https://www.mongodb.org/downloads)
4 | * SQL Server
5 | * You must create the database before running the apps (see the connection string in the config files)
6 |
7 | ### HashBus.Twitter.Monitor configuration [Optional]
8 |
9 | The `HashBus.Twitter.Monitor.Simulator` allows testing of most HashBus functionality without using the public [Twitter API](https://dev.twitter.com/overview/documentation). In order to run against the public Twitter API the following steps are required:
10 |
11 | * Go to https://apps.twitter.com/ and create a new application;
12 | * On the machine where the `HashBus.Twitter.Monitor` needs to run, create the following environment variables:
13 | * `HASHBUS_TWITTER_CONSUMER_KEY`: Twitter app consumer key
14 | * `HASHBUS_TWITTER_CONSUMER_SECRET`: Twitter app consumer secret
15 | * `HASHBUS_TWITTER_ACCESS_TOKEN`: Twitter app access token
16 | * `HASHBUS_TWITTER_ACCESS_TOKEN_SECRET`: Twitter app token secret
17 |
18 | ### Web API
19 |
20 | The web API is hosted at http://hashbus-demo.cloudapp.net:8080/
21 |
22 | Available resources are:
23 |
24 | * `http://hashbus-demo.cloudapp.net:8080/top-tweeters-retweeters/{track}`
25 | * `http://hashbus-demo.cloudapp.net:8080/top-tweeters/{track}`
26 | * `http://hashbus-demo.cloudapp.net:8080/top-retweeters/{track}`
27 | * `http://hashbus-demo.cloudapp.net:8080/most-mentioned/{track}`
28 | * `http://hashbus-demo.cloudapp.net:8080/most-retweeted/{track}`
29 | * `http://hashbus-demo.cloudapp.net:8080/most-hashtagged/{track}`
30 |
31 | A 'track' is a Twitter search term. At the time of writing, the HashBus Twitter monitor is running for the [#BuildStuffLT](https://twitter.com/search?q=%23BuildStuffLT) hashtag.
32 |
33 | Here comes the funky thing. There is a [bug in Nancy](https://github.com/NancyFx/Nancy/issues/1154) which prevents a `#` (hash/pound) sign from being used in a URL, even if URL encoded. For this reason we use a special character sequence `해시` to represent `#`. (해시 means "hash" in Korean!)
34 |
35 | Thus, example URL's for #BuildStuffLT are:
36 |
37 | * http://hashbus-demo.cloudapp.net:8080/top-tweeters-retweeters/해시BuildStuffLT
38 | * http://hashbus-demo.cloudapp.net:8080/top-tweeters/해시BuildStuffLT
39 | * http://hashbus-demo.cloudapp.net:8080/top-retweeters/해시BuildStuffLT
40 | * http://hashbus-demo.cloudapp.net:8080/most-mentioned/해시BuildStuffLT
41 | * http://hashbus-demo.cloudapp.net:8080/most-retweeted/해시BuildStuffLT
42 | * http://hashbus-demo.cloudapp.net:8080/most-hashtagged/해시BuildStuffLT
43 |
44 | These URL's will give you a leaderboard object which looks like this:
45 |
46 | ```JSON
47 | {
48 | "entries": [{
49 | "position": 1,
50 | "id": 1351703234,
51 | "idStr": "1351703234",
52 | "name": "Build Stuff 2015 LT",
53 | "screenName": "BuildStuffLT",
54 | "count": 28
55 | },
56 | {
57 | "position": 2,
58 | "id": 15528065,
59 | "idStr": "15528065",
60 | "name": "Malk’Zameth",
61 | "screenName": "malk_zameth",
62 | "count": 16
63 | },
64 | {
65 | "position": 3,
66 | "id": 183551266,
67 | "idStr": "183551266",
68 | "name": "Daniel Lee",
69 | "screenName": "danlimerick",
70 | "count": 15
71 | },
72 | {
73 | "position": 4,
74 | "id": 235599885,
75 | "idStr": "235599885",
76 | "name": "Peter Even",
77 | "screenName": "petervaneven",
78 | "count": 14
79 | },
80 | {
81 | "position": 5,
82 | "id": 22696598,
83 | "idStr": "22696598",
84 | "name": "Mauro Servienti",
85 | "screenName": "mauroservienti",
86 | "count": 10
87 | },
88 | {
89 | "position": 6,
90 | "id": 2511419816,
91 | "idStr": "2511419816",
92 | "name": "Jean-François Saguin",
93 | "screenName": "jfsaguin",
94 | "count": 9
95 | },
96 | {
97 | "position": 7,
98 | "id": 161837846,
99 | "idStr": "161837846",
100 | "name": "Bouillier Clément",
101 | "screenName": "clem_bouillier",
102 | "count": 7
103 | },
104 | {
105 | "position": 8,
106 | "id": 14128651,
107 | "idStr": "14128651",
108 | "name": "Grégory Weinbach",
109 | "screenName": "gweinbach",
110 | "count": 7
111 | },
112 | {
113 | "position": 9,
114 | "id": 8885582,
115 | "idStr": "8885582",
116 | "name": "Rui Carvalho",
117 | "screenName": "rhwy",
118 | "count": 6
119 | },
120 | {
121 | "position": 10,
122 | "id": 2375271441,
123 | "idStr": "2375271441",
124 | "name": "Ernestas Kardzys",
125 | "screenName": "ErnestasKardzys",
126 | "count": 6
127 | }],
128 | "count": 310,
129 | "since": "2015-11-17T16:17:41.0000000Z"
130 | }
131 | ```
132 |
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/HashBus.NServiceBusConfiguration/BusConfigurationExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.NServiceBusConfiguration
2 | {
3 | using NServiceBus;
4 |
5 | public static class BusConfigurationExtensions
6 | {
7 | public static void ApplyMessageConventions(this EndpointConfiguration configuration)
8 | {
9 | configuration.Conventions()
10 | .DefiningCommandsAs(t => t.Namespace != null && t.Namespace.EndsWith("Commands"))
11 | .DefiningEventsAs(t => t.Namespace != null && t.Namespace.EndsWith("Events"));
12 | }
13 |
14 | public static void ApplyErrorAndAuditQueueSettings(this EndpointConfiguration configuration)
15 | {
16 | configuration.AuditProcessedMessagesTo("audit");
17 | configuration.SendFailedMessagesTo("error");
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/HashBus.NServiceBusConfiguration/HashBus.NServiceBusConfiguration.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | latest
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostHashtagged/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.MostHashtagged
2 | {
3 | using System.Collections.Generic;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using HashBus.NServiceBusConfiguration;
7 | using HashBus.ReadModel;
8 | using HashBus.ReadModel.MongoDB;
9 | using MongoDB.Driver;
10 | using NServiceBus;
11 |
12 | class App
13 | {
14 | public static async Task Run(string mongoConnectionString, string mongoDBDatabase, string endpointName, string analyzerAddress)
15 | {
16 | var mongoDatabase = new MongoClient(mongoConnectionString).GetDatabase(mongoDBDatabase);
17 |
18 | var endpointConfiguration = new EndpointConfiguration(endpointName);
19 | endpointConfiguration.UseSerialization();
20 | endpointConfiguration.EnableInstallers();
21 | endpointConfiguration.UsePersistence();
22 | endpointConfiguration.RegisterComponents(c =>
23 | c.RegisterSingleton>>(
24 | new MongoDBListRepository(mongoDatabase, "most_hashtagged__hashtags")));
25 | endpointConfiguration.ApplyMessageConventions();
26 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
27 | endpointConfiguration.LimitMessageProcessingConcurrencyTo(1);
28 |
29 | var transportExtensions = endpointConfiguration.UseTransport();
30 |
31 | var routing = transportExtensions.Routing();
32 | routing.RegisterPublisher(typeof(Twitter.Analyzer.Events.TweetAnalyzed), analyzerAddress);
33 |
34 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
35 | .ConfigureAwait(false);
36 |
37 | try
38 | {
39 | await Task.Delay(Timeout.Infinite);
40 | }
41 | finally
42 | {
43 | await endpointInstance.Stop()
44 | .ConfigureAwait(false);
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostHashtagged/HashBus.Projector.MostHashtagged.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostHashtagged/MostHashtaggedProjection.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.MostHashtagged
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using ColoredConsole;
8 | using HashBus.ReadModel;
9 | using LiteGuard;
10 | using NServiceBus;
11 |
12 | public class MostHashtaggedProjection : IHandleMessages
13 | {
14 | private readonly IRepository> hashtags;
15 |
16 | public MostHashtaggedProjection(IRepository> hashtags)
17 | {
18 | Guard.AgainstNullArgument(nameof(hashtags), hashtags);
19 |
20 | this.hashtags = hashtags;
21 | }
22 |
23 | public async Task Handle(Twitter.Analyzer.Events.TweetAnalyzed message, IMessageHandlerContext context)
24 | {
25 | if (!message.Tweet.Hashtags.Any())
26 | {
27 | return;
28 | }
29 |
30 | var trackHashtags = (await this.hashtags.GetAsync(message.Tweet.Track)
31 | .ConfigureAwait(false)).ToList();
32 |
33 | if (trackHashtags.Any(hashtag => hashtag.TweetId == message.Tweet.Id))
34 | {
35 | return;
36 | }
37 |
38 | var newHashtags = message.Tweet.Hashtags.Select(hashtag =>
39 | new Hashtag
40 | {
41 | HashtaggedAt = message.Tweet.CreatedAt,
42 | TweetId = message.Tweet.Id,
43 | Text = hashtag.Text,
44 | })
45 | .ToList();
46 |
47 | trackHashtags.AddRange(newHashtags);
48 |
49 | await this.hashtags.SaveAsync(message.Tweet.Track, trackHashtags)
50 | .ConfigureAwait(false);
51 |
52 | foreach (var hashtag in newHashtags)
53 | {
54 | ColorConsole.WriteLine(
55 | $"{message.Tweet.CreatedAt.ToLocalTime()}".DarkGray(),
56 | " ",
57 | "Added ".Gray(),
58 | $"#{hashtag.Text}".Cyan(),
59 | " usage to ".Gray(),
60 | $" {message.Tweet.Track} ".DarkCyan().On(ConsoleColor.White));
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostHashtagged/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.MostHashtagged
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var mongoConnectionString = ConfigurationManager.AppSettings["MongoConnectionString"];
12 | var mongoDBDatabase = ConfigurationManager.AppSettings["MongoDBDatabase"];
13 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
14 | var analyzerAddress = ConfigurationManager.AppSettings["AnalyzerAddress"];
15 |
16 | Console.Title = typeof(Program).Assembly.GetName().Name;
17 |
18 | return App.Run(mongoConnectionString, mongoDBDatabase, endpointName, analyzerAddress);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostHashtagged/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostMentioned/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.MostMentioned
2 | {
3 | using System.Collections.Generic;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using HashBus.NServiceBusConfiguration;
7 | using HashBus.ReadModel;
8 | using HashBus.ReadModel.MongoDB;
9 | using MongoDB.Driver;
10 | using NServiceBus;
11 |
12 | class App
13 | {
14 | public static async Task Run(string mongoConnectionString, string mongoDBDatabase, string endpointName, string analyzerAddress)
15 | {
16 | var mongoDatabase = new MongoClient(mongoConnectionString).GetDatabase(mongoDBDatabase);
17 |
18 | var endpointConfiguration = new EndpointConfiguration(endpointName);
19 | endpointConfiguration.UseSerialization();
20 | endpointConfiguration.EnableInstallers();
21 | endpointConfiguration.UsePersistence();
22 | endpointConfiguration.RegisterComponents(c =>
23 | c.RegisterSingleton>>(
24 | new MongoDBListRepository(mongoDatabase, "most_mentioned__mentions")));
25 | endpointConfiguration.ApplyMessageConventions();
26 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
27 | endpointConfiguration.LimitMessageProcessingConcurrencyTo(1);
28 |
29 | var transportExtensions = endpointConfiguration.UseTransport();
30 |
31 | var routing = transportExtensions.Routing();
32 | routing.RegisterPublisher(typeof(Twitter.Analyzer.Events.TweetAnalyzed), analyzerAddress);
33 |
34 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
35 | .ConfigureAwait(false);
36 |
37 | try
38 | {
39 | await Task.Delay(Timeout.Infinite);
40 | }
41 | finally
42 | {
43 | await endpointInstance.Stop()
44 | .ConfigureAwait(false);
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostMentioned/HashBus.Projector.MostMentioned.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostMentioned/MostMentionedProjection.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.MostMentioned
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using ColoredConsole;
8 | using HashBus.Twitter.Analyzer.Events;
9 | using HashBus.ReadModel;
10 | using LiteGuard;
11 | using NServiceBus;
12 |
13 | public class MostMentionedProjection : IHandleMessages
14 | {
15 | private readonly IRepository> mentions;
16 |
17 | public MostMentionedProjection(IRepository> mentions)
18 | {
19 | Guard.AgainstNullArgument(nameof(mentions), mentions);
20 |
21 | this.mentions = mentions;
22 | }
23 |
24 | public async Task Handle(TweetAnalyzed message, IMessageHandlerContext context)
25 | {
26 | if (!message.Tweet.UserMentions.Any())
27 | {
28 | return;
29 | }
30 |
31 | var trackMentions = (await this.mentions.GetAsync(message.Tweet.Track)
32 | .ConfigureAwait(false)).ToList();
33 |
34 | if (trackMentions.Any(mention => mention.TweetId == message.Tweet.Id))
35 | {
36 | return;
37 | }
38 |
39 | var newMentions = message.Tweet.UserMentions.Select(userMention =>
40 | new Mention
41 | {
42 | MentionedAt = message.Tweet.CreatedAt,
43 | TweetId = message.Tweet.Id,
44 | UserMentionId = userMention.Id,
45 | UserMentionName = userMention.Name,
46 | UserMentionScreenName = userMention.ScreenName,
47 | })
48 | .ToList();
49 |
50 | trackMentions.AddRange(newMentions);
51 |
52 | await this.mentions.SaveAsync(message.Tweet.Track, trackMentions)
53 | .ConfigureAwait(false);
54 |
55 | foreach (var mention in newMentions)
56 | {
57 | ColorConsole.WriteLine(
58 | $"{message.Tweet.CreatedAt.ToLocalTime()}".DarkGray(),
59 | " ",
60 | "Added ".Gray(),
61 | $"@{mention.UserMentionScreenName}".Cyan(),
62 | " mention to ".Gray(),
63 | $" {message.Tweet.Track} ".DarkCyan().On(ConsoleColor.White));
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostMentioned/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.MostMentioned
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var mongoConnectionString = ConfigurationManager.AppSettings["MongoConnectionString"];
12 | var mongoDBDatabase = ConfigurationManager.AppSettings["MongoDBDatabase"];
13 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
14 | var analyzerAddress = ConfigurationManager.AppSettings["AnalyzerAddress"];
15 |
16 | Console.Title = typeof(Program).Assembly.GetName().Name;
17 |
18 | return App.Run(mongoConnectionString, mongoDBDatabase, endpointName, analyzerAddress);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostMentioned/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostRetweeted/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.MostRetweeted
2 | {
3 | using System.Collections.Generic;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using HashBus.NServiceBusConfiguration;
7 | using HashBus.ReadModel;
8 | using HashBus.ReadModel.MongoDB;
9 | using MongoDB.Driver;
10 | using NServiceBus;
11 |
12 | class App
13 | {
14 | public static async Task Run(string mongoConnectionString, string mongoDBDatabase, string endpointName, string analyzerAddress)
15 | {
16 | var mongoDatabase = new MongoClient(mongoConnectionString).GetDatabase(mongoDBDatabase);
17 |
18 | var endpointConfiguration = new EndpointConfiguration(endpointName);
19 | endpointConfiguration.UseSerialization();
20 | endpointConfiguration.EnableInstallers();
21 | endpointConfiguration.UsePersistence();
22 | endpointConfiguration.RegisterComponents(c =>
23 | c.RegisterSingleton>>(
24 | new MongoDBListRepository(mongoDatabase, "most_retweeted__retweetees")));
25 | endpointConfiguration.ApplyMessageConventions();
26 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
27 | endpointConfiguration.LimitMessageProcessingConcurrencyTo(1);
28 |
29 | var transportExtensions = endpointConfiguration.UseTransport();
30 |
31 | var routing = transportExtensions.Routing();
32 | routing.RegisterPublisher(typeof(Twitter.Analyzer.Events.TweetAnalyzed), analyzerAddress);
33 |
34 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
35 | .ConfigureAwait(false);
36 |
37 | try
38 | {
39 | await Task.Delay(Timeout.Infinite);
40 | }
41 | finally
42 | {
43 | await endpointInstance.Stop()
44 | .ConfigureAwait(false);
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostRetweeted/HashBus.Projector.MostRetweeted.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostRetweeted/MostRetweetedProjection.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.MostRetweeted
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using ColoredConsole;
8 | using HashBus.ReadModel;
9 | using LiteGuard;
10 | using NServiceBus;
11 |
12 | public class MostRetweetedProjection : IHandleMessages
13 | {
14 | private readonly IRepository> retweetees;
15 |
16 | public MostRetweetedProjection(IRepository> retweetees)
17 | {
18 | Guard.AgainstNullArgument(nameof(retweetees), retweetees);
19 |
20 | this.retweetees = retweetees;
21 | }
22 |
23 | public async Task Handle(Twitter.Analyzer.Events.TweetAnalyzed message, IMessageHandlerContext context)
24 | {
25 | if (message.Tweet.RetweetedTweet == null)
26 | {
27 | return;
28 | }
29 |
30 | var trackRetweetees = (await this.retweetees.GetAsync(message.Tweet.Track)
31 | .ConfigureAwait(false)).ToList();
32 |
33 | if (trackRetweetees.Any(tweet => tweet.TweetId == message.Tweet.Id))
34 | {
35 | return;
36 | }
37 |
38 | var retweetees = new List();
39 | var retweet = message.Tweet.RetweetedTweet;
40 | while (retweet != null)
41 | {
42 | retweetees.Add(new Retweetee
43 | {
44 | RetweetedAt = message.Tweet.CreatedAt,
45 | TweetId = retweet.Id,
46 | UserId = retweet.CreatedById,
47 | UserName = retweet.CreatedByName,
48 | UserScreenName = retweet.CreatedByScreenName,
49 | });
50 |
51 | retweet = retweet.RetweetedTweet;
52 | }
53 |
54 | trackRetweetees.AddRange(retweetees);
55 | await this.retweetees.SaveAsync(message.Tweet.Track, trackRetweetees)
56 | .ConfigureAwait(false);
57 |
58 | foreach (var retweetee in retweetees)
59 | {
60 | ColorConsole.WriteLine(
61 | $"{message.Tweet.CreatedAt.ToLocalTime()}".DarkGray(),
62 | " ",
63 | "Added retweet of ".Gray(),
64 | $"@{retweetee.UserScreenName}".Cyan(),
65 | " to ".Gray(),
66 | $" {message.Tweet.Track} ".DarkCyan().On(ConsoleColor.White));
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostRetweeted/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.MostRetweeted
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var mongoConnectionString = ConfigurationManager.AppSettings["MongoConnectionString"];
12 | var mongoDBDatabase = ConfigurationManager.AppSettings["MongoDBDatabase"];
13 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
14 | var analyzerAddress = ConfigurationManager.AppSettings["AnalyzerAddress"];
15 |
16 | Console.Title = typeof(Program).Assembly.GetName().Name;
17 |
18 | return App.Run(mongoConnectionString, mongoDBDatabase, endpointName, analyzerAddress);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.MostRetweeted/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopRetweeters/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.TopRetweeters
2 | {
3 | using System.Collections.Generic;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using HashBus.NServiceBusConfiguration;
7 | using HashBus.ReadModel;
8 | using HashBus.ReadModel.MongoDB;
9 | using MongoDB.Driver;
10 | using NServiceBus;
11 |
12 | class App
13 | {
14 | public static async Task Run(string mongoConnectionString, string mongoDBDatabase, string endpointName, string analyzerAddress)
15 | {
16 | var mongoDatabase = new MongoClient(mongoConnectionString).GetDatabase(mongoDBDatabase);
17 |
18 | var endpointConfiguration = new EndpointConfiguration(endpointName);
19 | endpointConfiguration.UseSerialization();
20 | endpointConfiguration.EnableInstallers();
21 | endpointConfiguration.UsePersistence();
22 | endpointConfiguration.RegisterComponents(c =>
23 | c.RegisterSingleton>>(
24 | new MongoDBListRepository(mongoDatabase, "top_retweeters__retweets")));
25 | endpointConfiguration.ApplyMessageConventions();
26 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
27 | endpointConfiguration.LimitMessageProcessingConcurrencyTo(1);
28 |
29 | var transportExtensions = endpointConfiguration.UseTransport();
30 |
31 | var routing = transportExtensions.Routing();
32 | routing.RegisterPublisher(typeof(Twitter.Analyzer.Events.TweetAnalyzed), analyzerAddress);
33 |
34 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
35 | .ConfigureAwait(false);
36 |
37 | try
38 | {
39 | await Task.Delay(Timeout.Infinite);
40 | }
41 | finally
42 | {
43 | await endpointInstance.Stop()
44 | .ConfigureAwait(false);
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopRetweeters/HashBus.Projector.TopRetweeters.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopRetweeters/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.TopRetweeters
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var mongoConnectionString = ConfigurationManager.AppSettings["MongoConnectionString"];
12 | var mongoDBDatabase = ConfigurationManager.AppSettings["MongoDBDatabase"];
13 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
14 | var analyzerAddress = ConfigurationManager.AppSettings["AnalyzerAddress"];
15 |
16 | Console.Title = typeof(Program).Assembly.GetName().Name;
17 |
18 | return App.Run(mongoConnectionString, mongoDBDatabase, endpointName, analyzerAddress);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopRetweeters/TopRetweetersProjection.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.TopRetweeters
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using ColoredConsole;
8 | using HashBus.ReadModel;
9 | using LiteGuard;
10 | using NServiceBus;
11 |
12 | public class TopRetweetersProjection : IHandleMessages
13 | {
14 | private readonly IRepository> tweets;
15 |
16 | public TopRetweetersProjection(IRepository> tweets)
17 | {
18 | Guard.AgainstNullArgument(nameof(tweets), tweets);
19 |
20 | this.tweets = tweets;
21 | }
22 |
23 | public async Task Handle(Twitter.Analyzer.Events.TweetAnalyzed message, IMessageHandlerContext context)
24 | {
25 | if (message.Tweet.RetweetedTweet == null)
26 | {
27 | return;
28 | }
29 |
30 | var trackTweets = (await this.tweets.GetAsync(message.Tweet.Track)
31 | .ConfigureAwait(false)).ToList();
32 |
33 | if (trackTweets.Any(tweet => tweet.TweetId == message.Tweet.Id))
34 | {
35 | return;
36 | }
37 |
38 | trackTweets.Add(new Retweet
39 | {
40 | RetweetedAt = message.Tweet.CreatedAt,
41 | TweetId = message.Tweet.Id,
42 | UserId = message.Tweet.CreatedById,
43 | UserName = message.Tweet.CreatedByName,
44 | UserScreenName = message.Tweet.CreatedByScreenName,
45 | });
46 |
47 | await this.tweets.SaveAsync(message.Tweet.Track, trackTweets)
48 | .ConfigureAwait(false);
49 |
50 | ColorConsole.WriteLine(
51 | $"{message.Tweet.CreatedAt.ToLocalTime()}".DarkGray(),
52 | " ",
53 | "Added ".Gray(),
54 | $"@{message.Tweet.CreatedByScreenName}".Cyan(),
55 | " retweet to ".Gray(),
56 | $" {message.Tweet.Track} ".DarkCyan().On(ConsoleColor.White));
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopRetweeters/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopTweeters/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.TopTweeters
2 | {
3 | using System.Collections.Generic;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using HashBus.NServiceBusConfiguration;
7 | using HashBus.ReadModel;
8 | using HashBus.ReadModel.MongoDB;
9 | using MongoDB.Driver;
10 | using NServiceBus;
11 |
12 | class App
13 | {
14 | public static async Task Run(string mongoConnectionString, string mongoDBDatabase, string endpointName, string analyzerAddress)
15 | {
16 | var mongoDatabase = new MongoClient(mongoConnectionString).GetDatabase(mongoDBDatabase);
17 |
18 | var endpointConfiguration = new EndpointConfiguration(endpointName);
19 | endpointConfiguration.UseSerialization();
20 | endpointConfiguration.EnableInstallers();
21 | endpointConfiguration.UsePersistence();
22 | endpointConfiguration.RegisterComponents(c =>
23 | c.RegisterSingleton>>(
24 | new MongoDBListRepository(mongoDatabase, "top_tweeters__tweets")));
25 | endpointConfiguration.ApplyMessageConventions();
26 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
27 | endpointConfiguration.LimitMessageProcessingConcurrencyTo(1);
28 |
29 | var transportExtensions = endpointConfiguration.UseTransport();
30 |
31 | var routing = transportExtensions.Routing();
32 | routing.RegisterPublisher(typeof(Twitter.Analyzer.Events.TweetAnalyzed), analyzerAddress);
33 |
34 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
35 | .ConfigureAwait(false);
36 |
37 | try
38 | {
39 | await Task.Delay(Timeout.Infinite);
40 | }
41 | finally
42 | {
43 | await endpointInstance.Stop()
44 | .ConfigureAwait(false);
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopTweeters/HashBus.Projector.TopTweeters.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopTweeters/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.TopTweeters
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var mongoConnectionString = ConfigurationManager.AppSettings["MongoConnectionString"];
12 | var mongoDBDatabase = ConfigurationManager.AppSettings["MongoDBDatabase"];
13 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
14 | var analyzerAddress = ConfigurationManager.AppSettings["AnalyzerAddress"];
15 |
16 | Console.Title = typeof(Program).Assembly.GetName().Name;
17 |
18 | return App.Run(mongoConnectionString, mongoDBDatabase, endpointName, analyzerAddress);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopTweeters/TopTweetersProjection.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.TopTweeters
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using ColoredConsole;
8 | using HashBus.ReadModel;
9 | using LiteGuard;
10 | using NServiceBus;
11 |
12 | public class TopTweetersProjection : IHandleMessages
13 | {
14 | private readonly IRepository> tweets;
15 |
16 | public TopTweetersProjection(IRepository> tweets)
17 | {
18 | Guard.AgainstNullArgument(nameof(tweets), tweets);
19 |
20 | this.tweets = tweets;
21 | }
22 |
23 | public async Task Handle(Twitter.Analyzer.Events.TweetAnalyzed message, IMessageHandlerContext context)
24 | {
25 | if (message.Tweet.RetweetedTweet != null)
26 | {
27 | return;
28 | }
29 |
30 | var trackTweets = (await this.tweets.GetAsync(message.Tweet.Track)
31 | .ConfigureAwait(false)).ToList();
32 |
33 | if (trackTweets.Any(tweet => tweet.TweetId == message.Tweet.Id))
34 | {
35 | return;
36 | }
37 |
38 | trackTweets.Add(new Tweet
39 | {
40 | TweetedAt = message.Tweet.CreatedAt,
41 | TweetId = message.Tweet.Id,
42 | UserId = message.Tweet.CreatedById,
43 | UserName = message.Tweet.CreatedByName,
44 | UserScreenName = message.Tweet.CreatedByScreenName,
45 | });
46 |
47 | await this.tweets.SaveAsync(message.Tweet.Track, trackTweets)
48 | .ConfigureAwait(false);
49 |
50 | ColorConsole.WriteLine(
51 | $"{message.Tweet.CreatedAt.ToLocalTime()}".DarkGray(),
52 | " ",
53 | "Added ".Gray(),
54 | $"@{message.Tweet.CreatedByScreenName}".Cyan(),
55 | " tweet to ".Gray(),
56 | $" {message.Tweet.Track} ".DarkCyan().On(ConsoleColor.White));
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopTweeters/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopTweetersRetweeters/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.TopTweetersRetweeters
2 | {
3 | using System.Collections.Generic;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using HashBus.NServiceBusConfiguration;
7 | using HashBus.ReadModel;
8 | using HashBus.ReadModel.MongoDB;
9 | using MongoDB.Driver;
10 | using NServiceBus;
11 |
12 | class App
13 | {
14 | public static async Task Run(string mongoConnectionString, string mongoDBDatabase, string endpointName, string analyzerAddress)
15 | {
16 | var mongoDatabase = new MongoClient(mongoConnectionString).GetDatabase(mongoDBDatabase);
17 |
18 | var endpointConfiguration = new EndpointConfiguration(endpointName);
19 | endpointConfiguration.UseSerialization();
20 | endpointConfiguration.EnableInstallers();
21 | endpointConfiguration.UsePersistence();
22 | endpointConfiguration.RegisterComponents(c =>
23 | c.RegisterSingleton>>(
24 | new MongoDBListRepository(mongoDatabase, "top_tweeters_retweeters__tweets_retweets")));
25 | endpointConfiguration.ApplyMessageConventions();
26 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
27 | endpointConfiguration.LimitMessageProcessingConcurrencyTo(1);
28 |
29 | var transportExtensions = endpointConfiguration.UseTransport();
30 |
31 | var routing = transportExtensions.Routing();
32 | routing.RegisterPublisher(typeof(Twitter.Analyzer.Events.TweetAnalyzed), analyzerAddress);
33 |
34 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
35 | .ConfigureAwait(false);
36 |
37 | try
38 | {
39 | await Task.Delay(Timeout.Infinite);
40 | }
41 | finally
42 | {
43 | await endpointInstance.Stop()
44 | .ConfigureAwait(false);
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopTweetersRetweeters/HashBus.Projector.TopTweetersRetweeters.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopTweetersRetweeters/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.TopTweetersRetweeters
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var mongoConnectionString = ConfigurationManager.AppSettings["MongoConnectionString"];
12 | var mongoDBDatabase = ConfigurationManager.AppSettings["MongoDBDatabase"];
13 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
14 | var analyzerAddress = ConfigurationManager.AppSettings["AnalyzerAddress"];
15 |
16 | Console.Title = typeof(Program).Assembly.GetName().Name;
17 |
18 | return App.Run(mongoConnectionString, mongoDBDatabase, endpointName, analyzerAddress);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopTweetersRetweeters/TopTweetersRetweetersProjection.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Projector.TopTweetersRetweeters
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using ColoredConsole;
8 | using HashBus.ReadModel;
9 | using LiteGuard;
10 | using NServiceBus;
11 |
12 | public class TopTweetersRetweetersProjection : IHandleMessages
13 | {
14 | private readonly IRepository> tweets;
15 |
16 | public TopTweetersRetweetersProjection(IRepository> tweets)
17 | {
18 | Guard.AgainstNullArgument(nameof(tweets), tweets);
19 |
20 | this.tweets = tweets;
21 | }
22 |
23 | public async Task Handle(Twitter.Analyzer.Events.TweetAnalyzed message, IMessageHandlerContext context)
24 | {
25 | var trackTweets = (await this.tweets.GetAsync(message.Tweet.Track)
26 | .ConfigureAwait(false)).ToList();
27 |
28 | if (trackTweets.Any(tweet => tweet.TweetId == message.Tweet.Id))
29 | {
30 | return;
31 | }
32 |
33 | trackTweets.Add(new TweetRetweet
34 | {
35 | TweetedRetweetedAt = message.Tweet.CreatedAt,
36 | TweetId = message.Tweet.Id,
37 | UserId = message.Tweet.CreatedById,
38 | UserName = message.Tweet.CreatedByName,
39 | UserScreenName = message.Tweet.CreatedByScreenName,
40 | });
41 |
42 | await this.tweets.SaveAsync(message.Tweet.Track, trackTweets)
43 | .ConfigureAwait(false);
44 |
45 | ColorConsole.WriteLine(
46 | $"{message.Tweet.CreatedAt.ToLocalTime()}".DarkGray(),
47 | " ",
48 | "Added ".Gray(),
49 | $"@{message.Tweet.CreatedByScreenName}".Cyan(),
50 | " tweet/retweet to ".Gray(),
51 | $" {message.Tweet.Track} ".DarkCyan().On(ConsoleColor.White));
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/HashBus.Projector.TopTweetersRetweeters/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/HashBus.ReadModel.MongoDB/HashBus.ReadModel.MongoDB.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | latest
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/HashBus.ReadModel.MongoDB/MongoDBListRepository.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.ReadModel.MongoDB
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using global::MongoDB.Bson.Serialization.Attributes;
7 | using global::MongoDB.Driver;
8 | using LiteGuard;
9 |
10 | public class MongoDBListRepository : IRepository>
11 | {
12 | private readonly IMongoCollection collection;
13 |
14 | public MongoDBListRepository(IMongoDatabase database, string collectionName)
15 | {
16 | Guard.AgainstNullArgument(nameof(database), database);
17 |
18 | this.collection = database.GetCollection(collectionName);
19 | }
20 |
21 | public async Task> GetAsync(string key)
22 | {
23 | key = key?.ToLowerInvariant();
24 | return (await this.collection.Find(doc => doc.Id == key).ToListAsync()).FirstOrDefault()?.Values
25 | ?? Enumerable.Empty();
26 | }
27 |
28 | public async Task SaveAsync(string key, IEnumerable value)
29 | {
30 | key = key?.ToLowerInvariant();
31 | await this.collection.ReplaceOneAsync(
32 | doc => doc.Id == key, new Document { Id = key, Values = value }, new UpdateOptions { IsUpsert = true });
33 | }
34 |
35 | private class Document
36 | {
37 | [BsonId]
38 | public string Id { get; set; }
39 |
40 | public IEnumerable Values { get; set; }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/HashBus.ReadModel/HashBus.ReadModel.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | latest
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/HashBus.ReadModel/Hashtag.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.ReadModel
2 | {
3 | using System;
4 |
5 | public class Hashtag
6 | {
7 | public DateTime HashtaggedAt { get; set; }
8 |
9 | public long TweetId { get; set; }
10 |
11 | public string Text { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/HashBus.ReadModel/IRepository.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.ReadModel
2 | {
3 | using System.Threading.Tasks;
4 |
5 | public interface IRepository
6 | {
7 | Task GetAsync(TKey key);
8 |
9 | Task SaveAsync(TKey key, TValue value);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/HashBus.ReadModel/Mention.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.ReadModel
2 | {
3 | using System;
4 |
5 | public class Mention
6 | {
7 | public DateTime MentionedAt { get; set; }
8 |
9 | public long TweetId { get; set; }
10 |
11 | public long? UserMentionId { get; set; }
12 |
13 | public string UserMentionName { get; set; }
14 |
15 | public string UserMentionScreenName { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/HashBus.ReadModel/Retweet.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.ReadModel
2 | {
3 | using System;
4 |
5 | public class Retweet
6 | {
7 | public DateTime RetweetedAt { get; set; }
8 |
9 | public long TweetId { get; set; }
10 |
11 | public long UserId { get; set; }
12 |
13 | public string UserName { get; set; }
14 |
15 | public string UserScreenName { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/HashBus.ReadModel/Retweetee.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.ReadModel
2 | {
3 | using System;
4 |
5 | public class Retweetee
6 | {
7 | public DateTime RetweetedAt { get; set; }
8 |
9 | public long TweetId { get; set; }
10 |
11 | public long UserId { get; set; }
12 |
13 | public string UserName { get; set; }
14 |
15 | public string UserScreenName { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/HashBus.ReadModel/Tweet.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.ReadModel
2 | {
3 | using System;
4 |
5 | public class Tweet
6 | {
7 | public DateTime TweetedAt { get; set; }
8 |
9 | public long TweetId { get; set; }
10 |
11 | public long UserId { get; set; }
12 |
13 | public string UserName { get; set; }
14 |
15 | public string UserScreenName { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/HashBus.ReadModel/TweetRetweet.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.ReadModel
2 | {
3 | using System;
4 |
5 | public class TweetRetweet
6 | {
7 | public DateTime TweetedRetweetedAt { get; set; }
8 |
9 | public long TweetId { get; set; }
10 |
11 | public long UserId { get; set; }
12 |
13 | public string UserName { get; set; }
14 |
15 | public string UserScreenName { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer.Commands/AnalyzeTweet.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer.Commands
2 | {
3 | public class AnalyzeTweet
4 | {
5 | public Tweet Tweet { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer.Commands/HashBus.Twitter.Analyzer.Commands.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | latest
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer.Commands/Hashtag.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer.Commands
2 | {
3 | public class Hashtag
4 | {
5 | public string Text { get; set; }
6 |
7 | public int[] Indices { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer.Commands/Tweet.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer.Commands
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | public class Tweet
7 | {
8 | public string Track { get; set; }
9 |
10 | public long Id { get; set; }
11 |
12 | public DateTime CreatedAt { get; set; }
13 |
14 | public long CreatedById { get; set; }
15 |
16 | public string CreatedByIdStr { get; set; }
17 |
18 | public string CreatedByName { get; set; }
19 |
20 | public string CreatedByScreenName { get; set; }
21 |
22 | public string Text { get; set; }
23 |
24 | public IList UserMentions { get; set; }
25 |
26 | public IList Hashtags { get; set; }
27 |
28 | public Tweet RetweetedTweet { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer.Commands/UserMention.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer.Commands
2 | {
3 | using System.Collections.Generic;
4 |
5 | public class UserMention
6 | {
7 | public long Id { get; set; }
8 |
9 | public string IdStr { get; set; }
10 |
11 | public IList Indices { get; set; }
12 |
13 | public string Name { get; set; }
14 |
15 | public string ScreenName { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer.Events/HashBus.Twitter.Analyzer.Events.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | latest
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer.Events/Hashtag.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer.Events
2 | {
3 | public class Hashtag
4 | {
5 | public string Text { get; set; }
6 |
7 | public int[] Indices { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer.Events/Tweet.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer.Events
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | public class Tweet
7 | {
8 | public string Track { get; set; }
9 |
10 | public long Id { get; set; }
11 |
12 | public DateTime CreatedAt { get; set; }
13 |
14 | public long CreatedById { get; set; }
15 |
16 | public string CreatedByName { get; set; }
17 |
18 | public string CreatedByScreenName { get; set; }
19 |
20 | public string Text { get; set; }
21 |
22 | public IList UserMentions { get; set; }
23 |
24 | public IList Hashtags { get; set; }
25 |
26 | public Tweet RetweetedTweet { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer.Events/TweetAnalyzed.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer.Events
2 | {
3 | public class TweetAnalyzed
4 | {
5 | public Tweet Tweet { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer.Events/UserMention.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer.Events
2 | {
3 | using System.Collections.Generic;
4 |
5 | public class UserMention
6 | {
7 | public long Id { get; set; }
8 |
9 | public IList Indices { get; set; }
10 |
11 | public string Name { get; set; }
12 |
13 | public string ScreenName { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer/AnalyzeTweetHandler.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer
2 | {
3 | using System;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using HashBus.Twitter.Analyzer.Commands;
7 | using HashBus.Twitter.Analyzer.Events;
8 | using NServiceBus;
9 |
10 | public class AnalyzeTweetHandler : IHandleMessages
11 | {
12 | public Task Handle(AnalyzeTweet message, IMessageHandlerContext context)
13 | {
14 | message.Tweet.UserMentions = message.Tweet.UserMentions
15 | .Where(userMention =>
16 | message.Tweet.CreatedById != userMention.Id &&
17 | message.Tweet.CreatedByIdStr != userMention.IdStr &&
18 | message.Tweet.CreatedByScreenName != userMention.ScreenName &&
19 | (message.Tweet.RetweetedTweet == null ||
20 | (message.Tweet.RetweetedTweet.CreatedById != userMention.Id &&
21 | message.Tweet.RetweetedTweet.CreatedByIdStr != userMention.IdStr &&
22 | message.Tweet.RetweetedTweet.CreatedByScreenName != userMention.ScreenName)) &&
23 | message.Tweet.Text.Substring(0, userMention.Indices[0]).Trim().ToUpperInvariant() != "RT")
24 | .GroupBy(userMention => userMention.Id)
25 | .Select(group => group.First())
26 | .ToList();
27 |
28 | message.Tweet.Hashtags = message.Tweet.Hashtags
29 | .Where(hashtag => !string.Equals($"#{hashtag.Text}", message.Tweet.Track, StringComparison.OrdinalIgnoreCase))
30 | .GroupBy(hashtag => hashtag.Text.ToUpperInvariant())
31 | .Select(group => group.First())
32 | .ToList();
33 |
34 | Writer.Write(message.Tweet);
35 |
36 | return context.Publish(new TweetAnalyzed { Tweet = TweetMapper.Map(message.Tweet) });
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer
2 | {
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using HashBus.NServiceBusConfiguration;
6 | using NServiceBus;
7 | using NServiceBus.Persistence;
8 |
9 | class App
10 | {
11 | public static async Task Run(string nserviceBusConnectionString, string endpointName)
12 | {
13 | var endpointConfiguration = new EndpointConfiguration(endpointName);
14 | endpointConfiguration.UseSerialization();
15 | endpointConfiguration.EnableInstallers();
16 | endpointConfiguration.UsePersistence().ConnectionString(nserviceBusConnectionString);
17 | endpointConfiguration.ApplyMessageConventions();
18 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
19 | endpointConfiguration.LimitMessageProcessingConcurrencyTo(1);
20 |
21 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
22 | .ConfigureAwait(false);
23 |
24 | try
25 | {
26 | await Task.Delay(Timeout.Infinite);
27 | }
28 | finally
29 | {
30 | await endpointInstance.Stop()
31 | .ConfigureAwait(false);
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer/HashBus.Twitter.Analyzer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var nserviceBusConnectionString = ConfigurationManager.AppSettings["NServiceBusConnectionString"];
12 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
13 |
14 | Console.Title = typeof(Program).Assembly.GetName().Name;
15 |
16 | return App.Run(nserviceBusConnectionString, endpointName);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer/TweetMapper.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Analyzer
2 | {
3 | using System.Linq;
4 | using HashBus.Twitter.Analyzer.Events;
5 |
6 | static class TweetMapper
7 | {
8 | public static Tweet Map(Commands.Tweet tweet)
9 | {
10 | return tweet == null
11 | ? null
12 | : new Tweet
13 | {
14 | CreatedAt = tweet.CreatedAt,
15 | CreatedById = tweet.CreatedById,
16 | CreatedByName = tweet.CreatedByName,
17 | CreatedByScreenName = tweet.CreatedByScreenName,
18 | Hashtags = tweet.Hashtags
19 | .Select(hashTag =>
20 | new Hashtag
21 | {
22 | Text = hashTag.Text,
23 | Indices = hashTag.Indices,
24 | })
25 | .ToList(),
26 | Id = tweet.Id,
27 | RetweetedTweet = Map(tweet.RetweetedTweet),
28 | Text = tweet.Text,
29 | Track = tweet.Track,
30 | UserMentions = tweet.UserMentions
31 | .Select(userMention =>
32 | new UserMention
33 | {
34 | Id = userMention.Id,
35 | Indices = userMention.Indices,
36 | Name = userMention.Name,
37 | ScreenName = userMention.ScreenName,
38 | })
39 | .ToList(),
40 | };
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Analyzer/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.BackFill/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.BackFill
2 | {
3 | using System.Threading.Tasks;
4 | using HashBus.NServiceBusConfiguration;
5 | using HashBus.Twitter.CatchUp.Commands;
6 | using NServiceBus;
7 | using NServiceBus.Persistence;
8 |
9 | class App
10 | {
11 | public static async Task Run(
12 | string nserviceBusConnectionString,
13 | string endpointName,
14 | string track,
15 | long tweetId,
16 | string catchUpAddress)
17 | {
18 | var endpointConfiguration = new EndpointConfiguration(endpointName);
19 | endpointConfiguration.UseSerialization();
20 | endpointConfiguration.EnableInstallers();
21 | endpointConfiguration.UsePersistence().ConnectionString(nserviceBusConnectionString);
22 | endpointConfiguration.ApplyMessageConventions();
23 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
24 |
25 | var transportExtensions = endpointConfiguration.UseTransport();
26 |
27 | var routing = transportExtensions.Routing();
28 | routing.RouteToEndpoint(typeof(StartCatchUp), catchUpAddress);
29 |
30 | var command = new StartCatchUp
31 | {
32 | EndpointName = endpointName,
33 | Track = track,
34 | TweetId = tweetId,
35 | };
36 |
37 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
38 | .ConfigureAwait(false);
39 |
40 | try
41 | {
42 |
43 | await endpointInstance.Send(command)
44 | .ConfigureAwait(false);
45 | }
46 | finally
47 | {
48 | await endpointInstance.Stop()
49 | .ConfigureAwait(false);
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.BackFill/HashBus.Twitter.BackFill.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.BackFill/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.BackFill
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var nserviceBusConnectionString = ConfigurationManager.AppSettings["NServiceBusConnectionString"];
12 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
13 | var track = ConfigurationManager.AppSettings["Track"];
14 | var tweetId = long.Parse(ConfigurationManager.AppSettings["TweetId"]);
15 | var catchUpAddress = ConfigurationManager.AppSettings["CatchUpAddress"];
16 |
17 | Console.Title = typeof(Program).Assembly.GetName().Name;
18 |
19 | return App.Run(nserviceBusConnectionString, endpointName, track, tweetId, catchUpAddress);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.BackFill/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp.Commands/HashBus.Twitter.CatchUp.Commands.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | latest
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp.Commands/StartCatchUp.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.CatchUp.Commands
2 | {
3 | public class StartCatchUp
4 | {
5 | public long TweetId { get; set; }
6 |
7 | public string Track { get; set; }
8 |
9 | public string EndpointName { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.CatchUp
2 | {
3 | using System;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using HashBus.NServiceBusConfiguration;
7 | using HashBus.Twitter.Analyzer.Commands;
8 | using HashBus.Twitter.CatchUp.Commands;
9 | using HashBus.Twitter.Monitor.Events;
10 | using NServiceBus;
11 | using NServiceBus.Persistence;
12 |
13 | class App
14 | {
15 | public static async Task Run(
16 | int maximumNumberOfTweetsPerCatchUp,
17 | TimeSpan defaultTransactionTimeout,
18 | string nserviceBusConnectionString,
19 | string endpointName,
20 | string consumerKey,
21 | string consumerSecret,
22 | string accessToken,
23 | string accessTokenSecret,
24 | string catchUpAddress,
25 | string analyzerAddress,
26 | string monitorAddress)
27 | {
28 | var endpointConfiguration = new EndpointConfiguration(endpointName);
29 |
30 | var transportExtensions = endpointConfiguration.UseTransport()
31 | .Transactions(TransportTransactionMode.ReceiveOnly);
32 |
33 | endpointConfiguration.UnitOfWork().WrapHandlersInATransactionScope(defaultTransactionTimeout);
34 | endpointConfiguration.UseSerialization();
35 | endpointConfiguration.EnableInstallers();
36 | endpointConfiguration.UsePersistence().ConnectionString(nserviceBusConnectionString);
37 | endpointConfiguration.ApplyMessageConventions();
38 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
39 | endpointConfiguration.RegisterComponents(c=>c.RegisterSingleton(
40 | new TweetService(maximumNumberOfTweetsPerCatchUp, consumerKey, consumerSecret, accessToken, accessTokenSecret)));
41 | endpointConfiguration.LimitMessageProcessingConcurrencyTo(1);
42 |
43 | var routing = transportExtensions.Routing();
44 | routing.RouteToEndpoint(typeof(StartCatchUp), catchUpAddress);
45 | routing.RouteToEndpoint(typeof(AnalyzeTweet), analyzerAddress);
46 | routing.RegisterPublisher(typeof(TweetReceived), monitorAddress);
47 |
48 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
49 | .ConfigureAwait(false);
50 |
51 | try
52 | {
53 | await Task.Delay(Timeout.Infinite);
54 | }
55 | finally
56 | {
57 | await endpointInstance.Stop()
58 | .ConfigureAwait(false);
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp/HashBus.Twitter.CatchUp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp/ITweetService.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.CatchUp
2 | {
3 | using System.Collections.Generic;
4 | using Tweetinvi.Core.Interfaces;
5 |
6 | public interface ITweetService
7 | {
8 | IEnumerable Get(string track, long sinceTweetId);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.CatchUp
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var maximumNumberOfTweetsPerCatchUp = int.Parse(ConfigurationManager.AppSettings["MaximumNumberOfTweetsPerCatchUp"]);
12 | var defaultTransactionTimeout = TimeSpan.Parse(ConfigurationManager.AppSettings["defaultTransactionTimeout"]);
13 | var nserviceBusConnectionString = ConfigurationManager.AppSettings["NServiceBusConnectionString"];
14 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
15 | var catchUpAddress = ConfigurationManager.AppSettings["CatchUpAddress"];
16 | var analyzerAddress = ConfigurationManager.AppSettings["AnalyzerAddress"];
17 | var monitorAddress = ConfigurationManager.AppSettings["MonitorAddress"];
18 |
19 | var consumerKeyName = "HASHBUS_TWITTER_CONSUMER_KEY";
20 | var consumerSecretKeyName = "HASHBUS_TWITTER_CONSUMER_SECRET";
21 | var accessTokenSecretKeyName = "HASHBUS_TWITTER_ACCESS_TOKEN_SECRET";
22 | var accessTokenKeyName = "HASHBUS_TWITTER_ACCESS_TOKEN";
23 |
24 | var consumerKey = Environment.GetEnvironmentVariable(consumerKeyName);
25 | if (consumerKey == null)
26 | {
27 | throw new Exception($"{consumerKeyName} enviroment variable is not set.");
28 | }
29 |
30 | var consumerSecret = Environment.GetEnvironmentVariable(consumerSecretKeyName);
31 | if (consumerSecret == null)
32 | {
33 | throw new Exception($"{consumerSecretKeyName} enviroment variable is not set.");
34 | }
35 |
36 | var accessToken = Environment.GetEnvironmentVariable(accessTokenKeyName);
37 | if (accessToken == null)
38 | {
39 | throw new Exception($"{accessTokenKeyName} enviroment variable is not set.");
40 | }
41 |
42 | var accessTokenSecret = Environment.GetEnvironmentVariable(accessTokenSecretKeyName);
43 | if (accessTokenSecret == null)
44 | {
45 | throw new Exception($"{accessTokenSecretKeyName} enviroment variable is not set.");
46 | }
47 |
48 | Console.Title = typeof(Program).Assembly.GetName().Name;
49 |
50 | return App.Run(
51 | maximumNumberOfTweetsPerCatchUp,
52 | defaultTransactionTimeout,
53 | nserviceBusConnectionString,
54 | endpointName,
55 | consumerKey,
56 | consumerSecret,
57 | accessToken,
58 | accessTokenSecret,
59 | catchUpAddress,
60 | analyzerAddress,
61 | monitorAddress);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp/StartCatchUpHandler.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.CatchUp
2 | {
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using ColoredConsole;
6 | using HashBus.Twitter.CatchUp.Commands;
7 | using NServiceBus;
8 |
9 | public class StartCatchUpHandler : IHandleMessages
10 | {
11 | private readonly ITweetService tweetService;
12 |
13 | public StartCatchUpHandler(ITweetService tweetService)
14 | {
15 | this.tweetService = tweetService;
16 | }
17 |
18 | public async Task Handle(StartCatchUp message, IMessageHandlerContext context)
19 | {
20 | ColorConsole.WriteLine(
21 | "Catching up on".Gray(),
22 | " ",
23 | $" {message.Track} ".DarkCyan().OnWhite(),
24 | " ",
25 | "tweets since tweet".Gray(),
26 | " ",
27 | $"{message.TweetId}".White());
28 |
29 | var count = 0;
30 | foreach(var analyzeTweet in
31 | this.tweetService.Get(message.Track, message.TweetId).Select(tweet => TweetMapper.Map(tweet, message.Track)))
32 | {
33 | ++count;
34 | Writer.Write(analyzeTweet.Tweet);
35 | await context.Send(analyzeTweet)
36 | .ConfigureAwait(false);
37 | }
38 |
39 | ColorConsole.WriteLine(
40 | "Found".Gray(),
41 | " ",
42 | $"{count:N0}".White(),
43 | " ",
44 | $" {message.Track} ".DarkCyan().OnWhite(),
45 | " ",
46 | $"tweet{(count == 1 ? "" : "s")} since tweet".Gray(),
47 | " ",
48 | $"{message.TweetId}".White());
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp/TweetReceivedSaga.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.CatchUp
2 | {
3 | using System;
4 | using System.Threading.Tasks;
5 | using ColoredConsole;
6 | using HashBus.Twitter.CatchUp.Commands;
7 | using HashBus.Twitter.Monitor.Events;
8 | using NServiceBus;
9 |
10 | public class TweetReceivedSaga : Saga,
11 | IAmStartedByMessages
12 | {
13 | public async Task Handle(TweetReceived message, IMessageHandlerContext context)
14 | {
15 | if (Data.PreviousSessionId != Guid.Empty && Data.PreviousSessionId != message.SessionId)
16 | {
17 | ColorConsole.WriteLine(
18 | $" {message.Track} ".DarkCyan().OnWhite(),
19 | " ",
20 | "needs catch up from tweet".Gray(),
21 | " ",
22 | $"{Data.PreviousTweetId}".White());
23 |
24 | await context.Send(new StartCatchUp
25 | {
26 | TweetId = Data.PreviousTweetId,
27 | Track = message.Track,
28 | })
29 | .ConfigureAwait(false);
30 | }
31 |
32 | Data.PreviousSessionId = message.SessionId;
33 | Data.Hashtag = message.Track;
34 | Data.PreviousTweetId = message.TweetId;
35 | }
36 |
37 | ///
38 | /// Track and EndpointNmae are a composite key, so this is handled in .
39 | /// >
40 | protected override void ConfigureHowToFindSaga(SagaPropertyMapper mapper)
41 | {
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp/TweetReceivedSagaData.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.CatchUp
2 | {
3 | using System;
4 | using NServiceBus;
5 |
6 | public class TweetReceivedSagaData : ContainSagaData
7 | {
8 | public virtual string Hashtag { get; set; }
9 |
10 | public virtual Guid PreviousSessionId { get; set; }
11 |
12 | public virtual long PreviousTweetId { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp/TweetReceivedSagaFinder.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.CatchUp
2 | {
3 | using System.Threading.Tasks;
4 | using HashBus.Twitter.Monitor.Events;
5 | using NServiceBus;
6 | using NServiceBus.Extensibility;
7 | using NServiceBus.Persistence;
8 | using NServiceBus.Sagas;
9 |
10 | class TweetReceivedSagaFinder : IFindSagas.Using
11 | {
12 | public Task FindBy(TweetReceived message, SynchronizedStorageSession storageSession, ReadOnlyContextBag context)
13 | {
14 | var tweetReceivedSagaData = storageSession.Session().QueryOver()
15 | .Where(d => d.Hashtag == message.Track)
16 | .SingleOrDefault();
17 |
18 | return Task.FromResult(tweetReceivedSagaData);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp/TweetService.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.CatchUp
2 | {
3 | using System.Collections.Generic;
4 | using Tweetinvi;
5 | using Tweetinvi.Core.Credentials;
6 | using Tweetinvi.Core.Interfaces;
7 | using Tweetinvi.Core.Parameters;
8 |
9 | class TweetService : ITweetService
10 | {
11 | private readonly int maximumNumberOfTweets;
12 |
13 | public TweetService(int maximumNumberOfTweets, string consumerKey, string consumerSecret, string accessToken, string accessTokenSecret)
14 | {
15 | this.maximumNumberOfTweets = maximumNumberOfTweets;
16 | Auth.SetCredentials(new TwitterCredentials(consumerKey, consumerSecret, accessToken, accessTokenSecret));
17 | }
18 |
19 | public IEnumerable Get(string track, long sinceTweetId)
20 | {
21 | return Search
22 | .SearchTweets(new TweetSearchParameters(track) { SinceId = sinceTweetId, MaximumNumberOfResults = this.maximumNumberOfTweets });
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.CatchUp/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor.Events/HashBus.Twitter.Monitor.Events.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | latest
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor.Events/TweetReceived.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Monitor.Events
2 | {
3 | using System;
4 |
5 | public class TweetReceived
6 | {
7 | public string Track { get; set; }
8 |
9 | public long TweetId { get; set; }
10 |
11 | public Guid SessionId { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor.Simulator/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Monitor.Simulator
2 | {
3 | using System.Threading.Tasks;
4 | using HashBus.NServiceBusConfiguration;
5 | using HashBus.Twitter.Analyzer.Commands;
6 | using NServiceBus;
7 | using NServiceBus.Persistence;
8 |
9 | class App
10 | {
11 | public static async Task Run(string nserviceBusConnectionString, string hashtag, string endpointName, string analyzerAddress)
12 | {
13 | var endpointConfiguration = new EndpointConfiguration(endpointName);
14 | endpointConfiguration.UseSerialization();
15 | endpointConfiguration.EnableInstallers();
16 | endpointConfiguration.UsePersistence().ConnectionString(nserviceBusConnectionString);
17 | endpointConfiguration.ApplyMessageConventions();
18 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
19 |
20 | var transportExtensions = endpointConfiguration.UseTransport();
21 |
22 | var routing = transportExtensions.Routing();
23 | routing.RouteToEndpoint(typeof(AnalyzeTweet), analyzerAddress);
24 |
25 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
26 | .ConfigureAwait(false);
27 |
28 | try
29 | {
30 | await Simulation.Start(endpointInstance, hashtag)
31 | .ConfigureAwait(false);
32 | }
33 | finally
34 | {
35 | await endpointInstance.Stop()
36 | .ConfigureAwait(false);
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor.Simulator/HashBus.Twitter.Monitor.Simulator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor.Simulator/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Monitor.Simulator
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var hashtag = ConfigurationManager.AppSettings["Hashtag"];
12 | var nserviceBusConnectionString = ConfigurationManager.AppSettings["NServiceBusConnectionString"];
13 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
14 | var analyzerAddress = ConfigurationManager.AppSettings["AnalyzerAddress"];
15 |
16 | Console.Title = typeof(Program).Assembly.GetName().Name;
17 |
18 | return App.Run(nserviceBusConnectionString, hashtag, endpointName, analyzerAddress);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor.Simulator/Simulation.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Monitor.Simulator
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Threading;
6 | using NServiceBus;
7 | using System.Linq;
8 | using System.Threading.Tasks;
9 | using HashBus.Twitter.Analyzer.Commands;
10 |
11 | class Simulation
12 | {
13 | public static async Task Start(IEndpointInstance endpointInstance, string hashtag)
14 | {
15 | var userNames = new[]
16 | {
17 | "Erik",
18 | "ama",
19 | "Karekin",
20 | "cosima",
21 | "Kamyrn",
22 | "ajay",
23 | "Timaios",
24 | "mila",
25 | "Odilia",
26 | "randi",
27 | "Kennard",
28 | "ilike",
29 | "Rab",
30 | "yolonda",
31 | "Ikaia",
32 | };
33 |
34 | var secondaryHashtags = new[]
35 | {
36 | "csharp",
37 | "fsharp",
38 | "nservicebus",
39 | "azure",
40 | "dotnet",
41 | "oss",
42 | "javascript",
43 | "microservices",
44 | "serverless",
45 | "ddd",
46 | "aspnetcore",
47 | };
48 |
49 | var random = new Random();
50 |
51 | while (true)
52 | {
53 | Thread.Sleep((int)Math.Pow(random.Next(6), 5));
54 | var now = DateTime.UtcNow;
55 | var track = $"#{hashtag}";
56 | var hashtagText = track;
57 | var secondaryHashtag = secondaryHashtags[random.Next(secondaryHashtags.Length)];
58 |
59 | if (now.Millisecond % 3 == 0)
60 | {
61 | hashtag = hashtag.ToLowerInvariant();
62 | hashtagText = hashtagText.ToLowerInvariant();
63 | secondaryHashtag = secondaryHashtag.ToLowerInvariant();
64 | }
65 |
66 | if (now.Millisecond % 7 == 0)
67 | {
68 | hashtag = hashtag.ToUpperInvariant();
69 | hashtagText = hashtagText.ToUpperInvariant();
70 | secondaryHashtag = secondaryHashtag.ToUpperInvariant();
71 | }
72 |
73 | var userId = random.Next(userNames.Length);
74 | var userMentionId = random.Next(userNames.Length);
75 | var userMentionIndex = random.Next(31) + 1;
76 | var retweetedUserId = random.Next(userNames.Length);
77 | var text = string.Join(
78 | string.Empty,
79 | Enumerable.Range(0, userMentionIndex - 1).Select(i => char.ConvertFromUtf32(random.Next(65, 128)))) +
80 | $" {userNames[userMentionId]} " +
81 | string.Join(
82 | string.Empty,
83 | Enumerable.Range(0, random.Next(32)).Select(i => char.ConvertFromUtf32(random.Next(65, 128)))) +
84 | $" {hashtagText}" +
85 | $" #{secondaryHashtag}";
86 |
87 | var message = new AnalyzeTweet
88 | {
89 | Tweet = new Tweet
90 | {
91 | Track = track,
92 | Id = now.Ticks,
93 | CreatedAt = now,
94 | CreatedById = userId,
95 | CreatedByIdStr = $"{userId}",
96 | CreatedByName = userNames[userId],
97 | CreatedByScreenName = userNames[userId],
98 | Text = text,
99 | UserMentions = new List
100 | {
101 | new UserMention
102 | {
103 | Id=userMentionId,
104 | IdStr= $"{userMentionId}",
105 | Indices = new List { userMentionIndex, userMentionIndex + userNames[userMentionId].Length, },
106 | Name = userNames[userMentionId],
107 | ScreenName = userNames[userMentionId],
108 | },
109 | },
110 | Hashtags = new List
111 | {
112 | new Hashtag
113 | {
114 | Text = hashtag,
115 | Indices = new[] { text.Length - $"{hashtagText} #{secondaryHashtag}".Length, text.Length - $" #{secondaryHashtag}".Length, },
116 | },
117 | new Hashtag
118 | {
119 | Text = secondaryHashtag,
120 | Indices = new[] { text.Length - $"#{secondaryHashtag}".Length, text.Length, },
121 | },
122 | },
123 | RetweetedTweet = now.Millisecond % 3 == 0
124 | ? new Tweet
125 | {
126 | Track = track,
127 | Id = now.AddDays(-1000).Ticks,
128 | CreatedAt = now.AddDays(-1000),
129 | CreatedById = retweetedUserId,
130 | CreatedByIdStr = $"{retweetedUserId}",
131 | CreatedByName = userNames[retweetedUserId],
132 | CreatedByScreenName = userNames[retweetedUserId],
133 | Text = text,
134 | UserMentions = new List(),
135 | Hashtags = new List(),
136 | RetweetedTweet = null,
137 | }
138 | : null,
139 | }
140 | };
141 |
142 | Writer.Write(message.Tweet);
143 | await endpointInstance.Send(message)
144 | .ConfigureAwait(false);
145 | }
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor.Simulator/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Monitor
2 | {
3 | using System.Threading.Tasks;
4 | using HashBus.NServiceBusConfiguration;
5 | using HashBus.Twitter.Analyzer.Commands;
6 | using NServiceBus;
7 | using NServiceBus.Persistence;
8 |
9 | class App
10 | {
11 | public static async Task Run(
12 | string nserviceBusConnectionString,
13 | string track,
14 | string consumerKey,
15 | string consumerSecret,
16 | string accessToken,
17 | string accessTokenSecret,
18 | string endpointName,
19 | string analyzerAddress)
20 | {
21 | var endpointConfiguration = new EndpointConfiguration(endpointName);
22 | endpointConfiguration.UseSerialization();
23 | endpointConfiguration.EnableInstallers();
24 | endpointConfiguration.UsePersistence().ConnectionString(nserviceBusConnectionString);
25 | endpointConfiguration.ApplyMessageConventions();
26 | endpointConfiguration.ApplyErrorAndAuditQueueSettings();
27 |
28 | var transportExtensions = endpointConfiguration.UseTransport();
29 |
30 | var routing = transportExtensions.Routing();
31 | routing.RouteToEndpoint(typeof(AnalyzeTweet), analyzerAddress);
32 |
33 | var endpointInstance = await Endpoint.Start(endpointConfiguration)
34 | .ConfigureAwait(false);
35 |
36 | try
37 | {
38 | await Monitoring.StartAsync(
39 | endpointInstance,
40 | track,
41 | consumerKey,
42 | consumerSecret,
43 | accessToken,
44 | accessTokenSecret)
45 | .ConfigureAwait(false);
46 | }
47 | finally
48 | {
49 | await endpointInstance.Stop()
50 | .ConfigureAwait(false);
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor/HashBus.Twitter.Monitor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor/Monitoring.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Monitor
2 | {
3 | using System;
4 | using System.Threading;
5 | using System.Threading.Tasks;
6 | using ColoredConsole;
7 | using HashBus.Twitter.Monitor.Events;
8 | using NServiceBus;
9 | using Tweetinvi;
10 | using Tweetinvi.Core.Credentials;
11 |
12 | class Monitoring
13 | {
14 | public static async Task StartAsync(
15 | IEndpointInstance endpointInstance,
16 | string track,
17 | string consumerKey,
18 | string consumerSecret,
19 | string accessToken,
20 | string accessTokenSecret)
21 | {
22 | var credentials = new TwitterCredentials(consumerKey, consumerSecret, accessToken, accessTokenSecret);
23 | while (true)
24 | {
25 | try
26 | {
27 | var stream = Stream.CreateFilteredStream(credentials);
28 | stream.AddTrack(track);
29 |
30 | var sessionId = Guid.NewGuid();
31 | stream.StreamStarted += (sender, args) =>
32 | {
33 | sessionId = Guid.NewGuid();
34 | ColorConsole.WriteLine(
35 | $"{DateTime.UtcNow.ToLocalTime()}".DarkGray(),
36 | " ",
37 | $" {track} ".DarkCyan().OnWhite(),
38 | " ",
39 | "stream started with session ID".Gray(),
40 | " ",
41 | $"{sessionId}".White());
42 | };
43 |
44 | stream.StreamStopped += (sender, args) => ColorConsole.WriteLine(
45 | $"{DateTime.UtcNow.ToLocalTime()} ".DarkGray(),
46 | $" {track} ".DarkCyan().OnWhite(),
47 | " stream stopped.".Red(),
48 | args.Exception == null ? string.Empty : $" {args.Exception.Message}".DarkRed());
49 |
50 | stream.MatchingTweetReceived += async (sender, e) =>
51 | {
52 | var analyzeTweet = TweetMapper.Map(e.Tweet, track);
53 | Writer.Write(analyzeTweet.Tweet);
54 | await endpointInstance.Send(analyzeTweet)
55 | .ConfigureAwait(false);
56 |
57 | var tweetReceived = new TweetReceived()
58 | {
59 | SessionId = sessionId,
60 | Track = track,
61 | TweetId = e.Tweet.Id
62 | };
63 | await endpointInstance.Publish(tweetReceived)
64 | .ConfigureAwait(false);
65 | };
66 |
67 | await stream.StartStreamMatchingAnyConditionAsync();
68 | }
69 | catch (Exception ex)
70 | {
71 | ColorConsole.WriteLine($"{DateTime.UtcNow.ToLocalTime()} ".DarkGray(), "Error listening to Twitter stream.".Red(), $" {ex.Message}".DarkRed());
72 | Thread.Sleep(1000);
73 | }
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter.Monitor
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Threading.Tasks;
6 |
7 | class Program
8 | {
9 | static Task Main()
10 | {
11 | var track = ConfigurationManager.AppSettings["Track"];
12 | var nserviceBusConnectionString = ConfigurationManager.AppSettings["NServiceBusConnectionString"];
13 | var endpointName = ConfigurationManager.AppSettings["EndpointName"];
14 | var analyzerAddress = ConfigurationManager.AppSettings["AnalyzerAddress"];
15 |
16 | var consumerKeyName = "HASHBUS_TWITTER_CONSUMER_KEY";
17 | var consumerSecretKeyName = "HASHBUS_TWITTER_CONSUMER_SECRET";
18 | var accessTokenSecretKeyName = "HASHBUS_TWITTER_ACCESS_TOKEN_SECRET";
19 | var accessTokenKeyName = "HASHBUS_TWITTER_ACCESS_TOKEN";
20 |
21 | var consumerKey = Environment.GetEnvironmentVariable(consumerKeyName);
22 | if (consumerKey == null)
23 | {
24 | throw new Exception($"{consumerKeyName} enviroment variable is not set.");
25 | }
26 |
27 | var consumerSecret = Environment.GetEnvironmentVariable(consumerSecretKeyName);
28 | if (consumerSecret == null)
29 | {
30 | throw new Exception($"{consumerSecretKeyName} enviroment variable is not set.");
31 | }
32 |
33 | var accessToken = Environment.GetEnvironmentVariable(accessTokenKeyName);
34 | if (accessToken == null)
35 | {
36 | throw new Exception($"{accessTokenKeyName} enviroment variable is not set.");
37 | }
38 |
39 | var accessTokenSecret = Environment.GetEnvironmentVariable(accessTokenSecretKeyName);
40 | if (accessTokenSecret == null)
41 | {
42 | throw new Exception($"{accessTokenSecretKeyName} enviroment variable is not set.");
43 | }
44 |
45 | Console.Title = typeof(Program).Assembly.GetName().Name;
46 |
47 | return App.Run(
48 | nserviceBusConnectionString,
49 | track,
50 | consumerKey,
51 | consumerSecret,
52 | accessToken,
53 | accessTokenSecret,
54 | endpointName,
55 | analyzerAddress);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor/TweetMapper.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Twitter
2 | {
3 | using System.Linq;
4 | using HashBus.Twitter.Analyzer.Commands;
5 | using Tweetinvi.Core.Interfaces;
6 |
7 | static class TweetMapper
8 | {
9 | public static AnalyzeTweet Map(ITweet tweet, string track)
10 | {
11 | return new AnalyzeTweet
12 | {
13 | Tweet = MapTweet(tweet, track)
14 | };
15 | }
16 |
17 | private static Tweet MapTweet(ITweet tweet, string track)
18 | {
19 | return tweet == null
20 | ? null
21 | : new Tweet
22 | {
23 | CreatedAt = tweet.CreatedAt,
24 | CreatedById = tweet.CreatedBy.Id,
25 | CreatedByIdStr = tweet.CreatedBy.IdStr,
26 | CreatedByName = tweet.CreatedBy.Name,
27 | CreatedByScreenName = tweet.CreatedBy.ScreenName,
28 | Hashtags = tweet.Hashtags
29 | .Select(hashtag =>
30 | new Hashtag
31 | {
32 | Text = hashtag.Text,
33 | Indices = hashtag.Indices,
34 | })
35 | .ToList(),
36 | Id = tweet.Id,
37 | RetweetedTweet = MapTweet(tweet.RetweetedTweet, track),
38 | Text = tweet.Text,
39 | Track = track,
40 | UserMentions = tweet.UserMentions
41 | .Where(userMention => userMention.Id.HasValue)
42 | .Select(userMention =>
43 | new UserMention
44 | {
45 | Id = userMention.Id.Value,
46 | IdStr = userMention.IdStr,
47 | Indices = userMention.Indices,
48 | Name = userMention.Name,
49 | ScreenName = userMention.ScreenName,
50 | })
51 | .ToList(),
52 | };
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor/Writer.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particular/HashBus/08184628814c8521ba2e37c9f8ac96286202ab52/src/HashBus.Twitter.Monitor/Writer.cs
--------------------------------------------------------------------------------
/src/HashBus.Twitter.Monitor/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using RestSharp;
9 |
10 | class App
11 | {
12 | public static async Task Run(
13 | string webApiBaseUrl,
14 | string track,
15 | int refreshInterval,
16 | bool showPercentages,
17 | int verticalPadding,
18 | int horizontalPadding,
19 | string[] views,
20 | int rotateInterval)
21 | {
22 | var client = new RestClient(webApiBaseUrl);
23 |
24 | var allViews = new Dictionary
25 | {
26 | {
27 | "MostHashtagged",
28 | MostHashtaggedLeaderBoardViewFactory.Create(
29 | track, refreshInterval,
30 | showPercentages,
31 | verticalPadding,
32 | horizontalPadding,
33 | client)
34 | },
35 | {
36 | "MostMentioned",
37 | MostMentionedLeaderBoardViewFactory.Create(
38 | track, refreshInterval,
39 | showPercentages,
40 | verticalPadding,
41 | horizontalPadding,
42 | client)
43 | },
44 | {
45 | "MostRetweeted",
46 | MostRetweetedLeaderBoardViewFactory.Create(
47 | track, refreshInterval,
48 | showPercentages,
49 | verticalPadding,
50 | horizontalPadding,
51 | client)
52 | },
53 | {
54 | "TopRetweeters",
55 | TopRetweetersLeaderBoardViewFactory.Create(
56 | track, refreshInterval,
57 | showPercentages,
58 | verticalPadding,
59 | horizontalPadding,
60 | client)
61 | },
62 | {
63 | "TopTweeters",
64 | TopTweetersLeaderBoardViewFactory.Create(
65 | track, refreshInterval,
66 | showPercentages,
67 | verticalPadding,
68 | horizontalPadding,
69 | client)
70 | },
71 | {
72 | "TopTweetersRetweeters",
73 | TopTweetersRetweetersLeaderBoardViewFactory.Create(
74 | track, refreshInterval,
75 | showPercentages,
76 | verticalPadding,
77 | horizontalPadding,
78 | client)
79 | },
80 | };
81 |
82 | var invalidViews = views.Where(view => !allViews.ContainsKey(view)).ToList();
83 | if (invalidViews.Any())
84 | {
85 | throw new ArgumentException($"View(s) not found: {string.Join(", ", invalidViews)}.", nameof(views));
86 | }
87 |
88 | while (true)
89 | {
90 | foreach (var view in views)
91 | {
92 | var cancellationTokenSource = new CancellationTokenSource();
93 | var task = allViews[view].RunAsync(cancellationTokenSource.Token);
94 | await Task.Delay(rotateInterval);
95 | cancellationTokenSource.Cancel();
96 | await task;
97 | }
98 | }
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/ColorTokenExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using ColoredConsole;
6 |
7 | static class ColorTokenExtensions
8 | {
9 | public static IList Trim(this IEnumerable source, int maxWidth)
10 | {
11 | IList tokens = new List(source);
12 | while (tokens.Sum(token => token.Text.Length) > maxWidth)
13 | {
14 | tokens = tokens
15 | .Reverse()
16 | .SkipWhile(token => token.Text.Length == 0)
17 | .Reverse()
18 | .ToList();
19 |
20 | var oldLastToken = tokens.Last();
21 | var newLastToken = new ColorToken(
22 | oldLastToken.Text.Substring(0, oldLastToken.Text.Length - 1),
23 | oldLastToken.Color,
24 | oldLastToken.BackgroundColor);
25 |
26 | tokens = tokens.Take(tokens.Count - 1).ToList();
27 | tokens.Add(newLastToken);
28 | }
29 |
30 | return tokens;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/ConsoleHelper.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using System;
4 | using System.Diagnostics;
5 | using System.Runtime.InteropServices;
6 |
7 | static class ConsoleHelper
8 | {
9 | internal static void MakeTopMost()
10 | {
11 | var currentWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
12 |
13 | SetWindowPos(currentWindowHandle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
14 | }
15 |
16 | private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
17 | private const UInt32 SWP_NOSIZE = 0x0001;
18 | private const UInt32 SWP_NOMOVE = 0x0002;
19 | private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
20 |
21 | [DllImport("user32.dll")]
22 | [return: MarshalAs(UnmanagedType.Bool)]
23 | private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/HashBus.Viewer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/IRunAsync.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | internal interface IRunAsync
7 | {
8 | Task RunAsync(CancellationToken cancellationToken);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/IService.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using System.Threading.Tasks;
4 |
5 | interface IService
6 | {
7 | Task GetAsync(TKey key);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/LeaderboardService.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using System;
4 | using System.Globalization;
5 | using System.Net;
6 | using System.Runtime.Remoting;
7 | using System.Threading.Tasks;
8 | using LiteGuard;
9 | using Newtonsoft.Json;
10 | using RestSharp;
11 |
12 | class LeaderboardService : IService>
13 | {
14 | private readonly IRestClient client;
15 | private readonly string resource;
16 |
17 | public LeaderboardService(IRestClient client, string resource)
18 | {
19 | Guard.AgainstNullArgument(nameof(client), client);
20 | Guard.AgainstNullArgument(nameof(resource), resource);
21 |
22 | this.client = client;
23 | this.resource = resource;
24 | }
25 |
26 | public async Task> GetAsync(string key)
27 | {
28 | // see https://github.com/NancyFx/Nancy/issues/1154
29 | key = Uri.EscapeDataString(key.Replace("#", "해시"));
30 | var request = new RestRequest(string.Format(CultureInfo.InvariantCulture, this.resource, key));
31 | var response = await this.client.ExecuteGetTaskAsync(request);
32 |
33 | if (response.StatusCode == 0)
34 | {
35 | throw new ServerException(response.ErrorMessage, response.ErrorException);
36 | }
37 |
38 | if (response.StatusCode != HttpStatusCode.OK)
39 | {
40 | throw new Exception($"The server returned: {(int)response.StatusCode} {response.StatusDescription}", response.ErrorException);
41 | }
42 |
43 | return JsonConvert.DeserializeObject>(response.Content);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/LeaderboardView.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using ColoredConsole;
9 |
10 | class LeaderboardView : IRunAsync where TEntry : WebApi.IEntry
11 | {
12 | private static readonly Dictionary movementTokens =
13 | new Dictionary
14 | {
15 | { int.MinValue, ">" },
16 | { -1, "^" },
17 | { 0, "=" },
18 | { 1, "v" },
19 | };
20 |
21 | private static readonly Dictionary movementColors =
22 | new Dictionary
23 | {
24 | { int.MinValue, ConsoleColor.Yellow },
25 | { -1, ConsoleColor.Green},
26 | { 0, ConsoleColor.Gray },
27 | { 1, ConsoleColor.Red },
28 | };
29 |
30 | private static readonly Dictionary movementBackgroundColors =
31 | new Dictionary
32 | {
33 | { int.MinValue, ConsoleColor.DarkYellow },
34 | { -1, ConsoleColor.DarkGreen },
35 | { 0, ConsoleColor.Black },
36 | { 1, ConsoleColor.DarkRed },
37 | };
38 |
39 | private readonly string track;
40 | private readonly int refreshInterval;
41 | private readonly IService> leaderboards;
42 | private readonly bool showPercentages;
43 | private readonly int verticalPadding;
44 | private readonly int horizontalPadding;
45 | private readonly Func matchEntries;
46 | private readonly Func> getText;
47 | private readonly string name;
48 | private readonly string itemsName;
49 |
50 | private WebApi.Leaderboard previousLeaderboard = new WebApi.Leaderboard();
51 |
52 | public LeaderboardView(
53 | string track,
54 | int refreshInterval,
55 | IService> leaderboards,
56 | bool showPercentages,
57 | int verticalPadding,
58 | int horizontalPadding,
59 | Func matchEntries,
60 | Func> getText,
61 | string name,
62 | string itemsName)
63 | {
64 | this.track = track;
65 | this.refreshInterval = refreshInterval;
66 | this.leaderboards = leaderboards;
67 | this.showPercentages = showPercentages;
68 | this.verticalPadding = verticalPadding;
69 | this.horizontalPadding = horizontalPadding;
70 | this.matchEntries = matchEntries;
71 | this.getText = getText;
72 | this.name = name;
73 | this.itemsName = itemsName;
74 | }
75 |
76 | public Task RunAsync()
77 | {
78 | return this.RunAsync(new CancellationTokenSource().Token);
79 | }
80 |
81 | public async Task RunAsync(CancellationToken cancellationToken)
82 | {
83 | Console.CursorVisible = false;
84 | while (!cancellationToken.IsCancellationRequested)
85 | {
86 | WebApi.Leaderboard currentLeaderboard;
87 | try
88 | {
89 | currentLeaderboard = await leaderboards.GetAsync(track);
90 | }
91 | catch (Exception)
92 | {
93 | currentLeaderboard = previousLeaderboard;
94 | }
95 |
96 | var lines = new List>();
97 | foreach (var currentEntry in currentLeaderboard?.Entries ??
98 | Enumerable.Empty())
99 | {
100 | var previousEntry = (previousLeaderboard?.Entries ??
101 | Enumerable.Empty())
102 | .FirstOrDefault(e => matchEntries(e, currentEntry));
103 |
104 | var movement = previousEntry == null
105 | ? int.MinValue
106 | : Math.Sign(currentEntry.Position - previousEntry.Position);
107 |
108 | var countMovement = Math.Sign(Math.Min(previousEntry?.Count - currentEntry.Count ?? 0, movement));
109 |
110 | var tokens = new List
111 | {
112 | $"{movementTokens[movement]} {currentEntry.Position.ToString().PadLeft(2)}".Color(movementColors[movement]),
113 | };
114 |
115 | tokens.AddRange(getText(currentEntry));
116 | tokens.Add($" {currentEntry.Count:N0}".Color(movementColors[countMovement]));
117 |
118 | if (showPercentages)
119 | {
120 | tokens.Add($" ({currentEntry.Count / (double)currentLeaderboard.Count:P0})".DarkGray());
121 | }
122 |
123 | var maxWidth = Console.WindowWidth - (horizontalPadding * 2);
124 | tokens.Add(new string(' ', Math.Max(0, maxWidth - tokens.Sum(token => token.Text.Length))));
125 |
126 | lines.Add(tokens.Trim(maxWidth).Select(token => token.On(movementBackgroundColors[movement])));
127 | }
128 |
129 | Console.Clear();
130 | for (var newLine = verticalPadding - 1; newLine >= 0; newLine--)
131 | {
132 | ColorConsole.WriteLine();
133 | }
134 |
135 | var padding = new string(' ', horizontalPadding);
136 | ColorConsole.Write(
137 | padding,
138 | $" {track} ".DarkCyan().On(ConsoleColor.White),
139 | " ",
140 | name.White());
141 |
142 | if (currentLeaderboard == previousLeaderboard)
143 | {
144 | ColorConsole.Write(" ", currentLeaderboard == previousLeaderboard ? " Service unavailable ".Yellow().OnRed() : "");
145 | }
146 |
147 | ColorConsole.WriteLine();
148 |
149 | ColorConsole.WriteLine(
150 | padding,
151 | "Powered by ".DarkGray(),
152 | " NServiceBus ".White().OnDarkBlue(),
153 | " from ".DarkGray(),
154 | "Particular Software".White());
155 |
156 | ColorConsole.WriteLine();
157 | foreach (var line in lines)
158 | {
159 | ColorConsole.WriteLine(new ColorToken[] { padding }.Concat(line).ToArray());
160 | }
161 |
162 | ColorConsole.WriteLine();
163 |
164 | var totalColor = currentLeaderboard?.Count - previousLeaderboard?.Count > 0 ? movementColors[-1] : movementColors[0];
165 | ColorConsole.Write(padding, $"{currentLeaderboard?.Count ?? 0:N0} ".Color(totalColor), $"{itemsName}".DarkGray());
166 |
167 | if (currentLeaderboard.Since.HasValue)
168 | {
169 | ColorConsole.WriteLine(
170 | $" since ".DarkGray(),
171 | $"{currentLeaderboard.Since?.ToLocalTime():dddd} {currentLeaderboard.Since?.ToLocalTime():HH:mm}".Gray());
172 | }
173 |
174 | var maxMessageLength = 0;
175 | var refreshTime = DateTime.UtcNow.AddMilliseconds(refreshInterval);
176 | using (var timer = new Timer(c =>
177 | {
178 | var timeLeft = new TimeSpan(0, 0, 0, (int)Math.Round((refreshTime - DateTime.UtcNow).TotalSeconds));
179 | if (timeLeft.TotalSeconds == 0)
180 | {
181 | return;
182 | }
183 |
184 | var tokens = new[]
185 | {
186 | $"\r{padding}github.com/Particular/HashBus".Cyan(),
187 | $" · Refreshing in {timeLeft.TotalSeconds}...".DarkGray(),
188 | };
189 |
190 | var currentLength = tokens.Sum(x => x.Text.Length);
191 | maxMessageLength = Math.Max(maxMessageLength, currentLength);
192 | tokens = tokens.Concat(new ColorToken[] { new string(' ', maxMessageLength - currentLength) }).ToArray();
193 | ColorConsole.Write(tokens);
194 | }))
195 | {
196 | timer.Change(0, 1000);
197 | await Task.Delay(refreshInterval);
198 | }
199 |
200 | previousLeaderboard = currentLeaderboard;
201 | }
202 | }
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/MostHashtaggedLeaderBoardViewFactory.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using System;
4 | using ColoredConsole;
5 | using HashBus.WebApi;
6 | using RestSharp;
7 |
8 | static class MostHashtaggedLeaderBoardViewFactory
9 | {
10 | public static LeaderboardView Create(
11 | string track,
12 | int refreshInterval,
13 | bool showPercentages,
14 | int verticalPadding,
15 | int horizontalPadding,
16 | IRestClient client)
17 | {
18 | return new LeaderboardView(
19 | track,
20 | refreshInterval,
21 | new LeaderboardService(client, "/most-hashtagged/{0}"),
22 | showPercentages,
23 | verticalPadding,
24 | horizontalPadding,
25 | (entry1, entry2) => string.Equals(entry1.Text, entry2.Text, StringComparison.InvariantCultureIgnoreCase),
26 | entry => new[] { $" #{entry.Text}".Cyan(), },
27 | "Most Hashtagged",
28 | "hashtag usages");
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/MostMentionedLeaderBoardViewFactory.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using ColoredConsole;
4 | using HashBus.WebApi;
5 | using RestSharp;
6 |
7 | static class MostMentionedLeaderBoardViewFactory
8 | {
9 | public static LeaderboardView Create(
10 | string track,
11 | int refreshInterval,
12 | bool showPercentages,
13 | int verticalPadding,
14 | int horizontalPadding,
15 | IRestClient client)
16 | {
17 | return new LeaderboardView(
18 | track,
19 | refreshInterval,
20 | new LeaderboardService(client, "/most-mentioned/{0}"),
21 | showPercentages,
22 | verticalPadding,
23 | horizontalPadding,
24 | (entry1, entry2) => entry1.Id == entry2.Id,
25 | entry => new[] { $" {entry.Name}".White(), $" @{entry.ScreenName}".Cyan(), },
26 | "Most Mentioned",
27 | "mentions");
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/MostRetweetedLeaderBoardViewFactory.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using ColoredConsole;
4 | using HashBus.WebApi;
5 | using RestSharp;
6 |
7 | static class MostRetweetedLeaderBoardViewFactory
8 | {
9 | public static LeaderboardView Create(
10 | string track,
11 | int refreshInterval,
12 | bool showPercentages,
13 | int verticalPadding,
14 | int horizontalPadding,
15 | IRestClient client)
16 | {
17 | return new LeaderboardView(
18 | track,
19 | refreshInterval,
20 | new LeaderboardService(client, "/most-retweeted/{0}"),
21 | showPercentages,
22 | verticalPadding,
23 | horizontalPadding,
24 | (entry1, entry2) => entry1.Id == entry2.Id,
25 | entry => new[] { $" {entry.Name}".White(), $" @{entry.ScreenName}".Cyan(), },
26 | "Most Retweeted",
27 | "retweets");
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | class Program
10 | {
11 | static Task Main()
12 | {
13 | var webApiBaseUrl = ConfigurationManager.AppSettings["WebApiBaseUrl"];
14 | var track = ConfigurationManager.AppSettings["Track"];
15 | var refreshInterval = int.Parse(ConfigurationManager.AppSettings["refreshInterval"]);
16 | var showPercentages = bool.Parse(ConfigurationManager.AppSettings["ShowPercentages"]);
17 | var verticalPadding = int.Parse(ConfigurationManager.AppSettings["VerticalPadding"]);
18 | var horizontalPadding = int.Parse(ConfigurationManager.AppSettings["HorizontalPadding"]);
19 | var rotateInterval = int.Parse(ConfigurationManager.AppSettings["RotateInterval"]);
20 | var views = ConfigurationManager.AppSettings["Views"]
21 | .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
22 | .Select(view => view.Trim())
23 | .ToArray();
24 |
25 | Console.OutputEncoding = Encoding.UTF8;
26 | Console.Title = typeof(Program).Assembly.GetName().Name;
27 | ConsoleHelper.MakeTopMost();
28 |
29 | return App.Run(
30 | webApiBaseUrl,
31 | track,
32 | refreshInterval,
33 | showPercentages,
34 | verticalPadding,
35 | horizontalPadding,
36 | views,
37 | rotateInterval);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/TopRetweetersLeaderBoardViewFactory.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using ColoredConsole;
4 | using HashBus.WebApi;
5 | using RestSharp;
6 |
7 | static class TopRetweetersLeaderBoardViewFactory
8 | {
9 | public static LeaderboardView Create(
10 | string track,
11 | int refreshInterval,
12 | bool showPercentages,
13 | int verticalPadding,
14 | int horizontalPadding,
15 | IRestClient client)
16 | {
17 | return new LeaderboardView(
18 | track,
19 | refreshInterval,
20 | new LeaderboardService(client, "/top-retweeters/{0}"),
21 | showPercentages,
22 | verticalPadding,
23 | horizontalPadding,
24 | (entry1, entry2) => entry1.Id == entry2.Id,
25 | entry => new[] { $" {entry.Name}".White(), $" @{entry.ScreenName}".Cyan(), },
26 | "Top Retweeters",
27 | "retweets");
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/TopTweetersLeaderBoardViewFactory.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using ColoredConsole;
4 | using HashBus.WebApi;
5 | using RestSharp;
6 |
7 | static class TopTweetersLeaderBoardViewFactory
8 | {
9 | public static LeaderboardView Create(
10 | string track,
11 | int refreshInterval,
12 | bool showPercentages,
13 | int verticalPadding,
14 | int horizontalPadding,
15 | IRestClient client)
16 | {
17 | return new LeaderboardView(
18 | track,
19 | refreshInterval,
20 | new LeaderboardService(client, "/top-tweeters/{0}"),
21 | showPercentages,
22 | verticalPadding,
23 | horizontalPadding,
24 | (entry1, entry2) => entry1.Id == entry2.Id,
25 | entry => new[] { $" {entry.Name}".White(), $" @{entry.ScreenName}".Cyan(), },
26 | "Top Tweeters",
27 | "tweets");
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/TopTweetersRetweetersLeaderBoardViewFactory.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.Viewer
2 | {
3 | using ColoredConsole;
4 | using HashBus.WebApi;
5 | using RestSharp;
6 |
7 | static class TopTweetersRetweetersLeaderBoardViewFactory
8 | {
9 | public static LeaderboardView Create(
10 | string track,
11 | int refreshInterval,
12 | bool showPercentages,
13 | int verticalPadding,
14 | int horizontalPadding,
15 | IRestClient client)
16 | {
17 | return new LeaderboardView(
18 | track,
19 | refreshInterval,
20 | new LeaderboardService(client, "/top-tweeters-retweeters/{0}"),
21 | showPercentages,
22 | verticalPadding,
23 | horizontalPadding,
24 | (entry1, entry2) => entry1.Id == entry2.Id,
25 | entry => new[] { $" {entry.Name}".White(), $" @{entry.ScreenName}".Cyan(), },
26 | "Top Tweeters/Retweeters",
27 | "tweets/retweets");
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/HashBus.Viewer/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/App.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading;
7 | using ColoredConsole;
8 | using HashBus.ReadModel;
9 | using HashBus.ReadModel.MongoDB;
10 | using MongoDB.Driver;
11 | using Nancy;
12 | using Nancy.Bootstrapper;
13 | using Nancy.Hosting.Self;
14 | using Nancy.Responses.Negotiation;
15 | using Nancy.TinyIoc;
16 |
17 | class App
18 | {
19 | public static void Run(
20 | Uri baseUri, string mongoConnectionString, string mongoDBDatabase, IEnumerable ignoredUserNames, IEnumerable ignoredHashtags)
21 | {
22 | var bootstrapper = new Bootstrapper(
23 | new MongoClient(mongoConnectionString).GetDatabase(mongoDBDatabase),
24 | new IgnoredUserNamesService(ignoredUserNames),
25 | new IgnoredHashtagsService(ignoredHashtags));
26 |
27 | var hostConfiguration = new HostConfiguration
28 | {
29 | UrlReservations = new UrlReservations { CreateAutomatically = true },
30 | };
31 |
32 | using (var host = new NancyHost(bootstrapper, hostConfiguration, baseUri))
33 | {
34 | host.Start();
35 | ColorConsole.WriteLine("Web API hosted at ".Gray(), $"{baseUri}".White());
36 | ColorConsole.Write("Powered by ".DarkGray(), " Nancy ".Black().OnWhite());
37 | Thread.Sleep(Timeout.Infinite);
38 | }
39 | }
40 |
41 | private class Bootstrapper : DefaultNancyBootstrapper
42 | {
43 | private readonly IMongoDatabase mongoDatabase;
44 | private readonly IIgnoredUserNamesService ignoredUserNamesService;
45 | private readonly IIgnoredHashtagsService ignoredHashtagsService;
46 |
47 | public Bootstrapper(IMongoDatabase mongoDatabase, IIgnoredUserNamesService ignoredUserNamesService, IIgnoredHashtagsService ignoredHashtagsService)
48 | {
49 | this.mongoDatabase = mongoDatabase;
50 | this.ignoredUserNamesService = ignoredUserNamesService;
51 | this.ignoredHashtagsService = ignoredHashtagsService;
52 | }
53 |
54 | protected override NancyInternalConfiguration InternalConfiguration
55 | {
56 | get
57 | {
58 | return NancyInternalConfiguration.WithOverrides(configuration =>
59 | configuration.ResponseProcessors = new[] { typeof(JsonProcessor) });
60 | }
61 | }
62 |
63 | protected override void ConfigureApplicationContainer(TinyIoCContainer container)
64 | {
65 | base.ConfigureApplicationContainer(container);
66 |
67 | container.Register(this.ignoredUserNamesService);
68 | container.Register(this.ignoredHashtagsService);
69 |
70 | container.Register>>(
71 | new MongoDBListRepository(this.mongoDatabase, "most_mentioned__mentions"));
72 |
73 | container.Register>>(
74 | new MongoDBListRepository(this.mongoDatabase, "top_tweeters__tweets"));
75 |
76 | container.Register>>(
77 | new MongoDBListRepository(this.mongoDatabase, "top_tweeters_retweeters__tweets_retweets"));
78 |
79 | container.Register>>(
80 | new MongoDBListRepository(this.mongoDatabase, "top_retweeters__retweets"));
81 |
82 | container.Register>>(
83 | new MongoDBListRepository(this.mongoDatabase, "most_retweeted__retweetees"));
84 |
85 | container.Register>>(
86 | new MongoDBListRepository(this.mongoDatabase, "most_hashtagged__hashtags"));
87 | }
88 |
89 | protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
90 | {
91 | base.RequestStartup(container, pipelines, context);
92 |
93 | pipelines.AfterRequest.AddItemToEndOfPipeline(ctx =>
94 | {
95 | if (ctx.Request.Headers.Keys.Contains("Origin"))
96 | {
97 | ctx.Response.Headers["Access-Control-Allow-Origin"] =
98 | string.Join(" ", ctx.Request.Headers["Origin"]);
99 |
100 | if (ctx.Request.Method == "OPTIONS")
101 | {
102 | ctx.Response.Headers["Access-Control-Allow-Methods"] =
103 | "GET, POST, PUT, DELETE, OPTIONS";
104 |
105 | if (ctx.Request.Headers.Keys.Contains("Access-Control-Request-Headers"))
106 | {
107 | ctx.Response.Headers["Access-Control-Allow-Headers"] =
108 | string.Join(", ", ctx.Request.Headers["Access-Control-Request-Headers"]);
109 | }
110 | }
111 | }
112 | });
113 | }
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/HashBus.WebApi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net461
5 | Exe
6 | latest
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/HashtagEntry.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | public class HashtagEntry : IEntry
4 | {
5 | public int Position { get; set; }
6 |
7 | public string Text { get; set; }
8 |
9 | public int Count { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/IEntry.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | public interface IEntry
4 | {
5 | int Count { get; set; }
6 |
7 | int Position { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/IIgnoredHashtagsService.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System.Collections.Generic;
4 |
5 | public interface IIgnoredHashtagsService
6 | {
7 | IReadOnlyList Get();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/IIgnoredUserNamesService.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System.Collections.Generic;
4 |
5 | public interface IIgnoredUserNamesService
6 | {
7 | IReadOnlyList Get();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/IgnoredHashtagsService.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using LiteGuard;
6 |
7 | class IgnoredHashtagsService : IIgnoredHashtagsService
8 | {
9 | private readonly List ignoredHashtags;
10 |
11 | public IgnoredHashtagsService(IEnumerable ignoredHashtags)
12 | {
13 | Guard.AgainstNullArgument(nameof(ignoredHashtags), ignoredHashtags);
14 |
15 | this.ignoredHashtags = ignoredHashtags.ToList();
16 | }
17 |
18 | public IReadOnlyList Get()
19 | {
20 | return this.ignoredHashtags.ToList();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/IgnoredUserNamesService.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using LiteGuard;
6 |
7 | class IgnoredUserNamesService : IIgnoredUserNamesService
8 | {
9 | private readonly List ignoredUserNames;
10 |
11 | public IgnoredUserNamesService(IEnumerable ignoredUserNames)
12 | {
13 | Guard.AgainstNullArgument(nameof(ignoredUserNames), ignoredUserNames);
14 |
15 | this.ignoredUserNames = ignoredUserNames.ToList();
16 | }
17 |
18 | public IReadOnlyList Get()
19 | {
20 | return this.ignoredUserNames.ToList();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/Leaderboard.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | class Leaderboard
7 | {
8 | public IList Entries { get; set; }
9 |
10 | public int Count { get; set; }
11 |
12 | public DateTime? Since { get; set; }
13 |
14 | public DateTime? LastActivityDateTime { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/MostHashtaggedModule.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using HashBus.ReadModel;
7 | using Nancy;
8 |
9 | public class MostHashtaggedModule : NancyModule
10 | {
11 | public MostHashtaggedModule(IRepository> hashtags, IIgnoredHashtagsService ignoredHashtagsService)
12 | {
13 | this.Get["/most-hashtagged/{track}", true] = async (parameters, __) =>
14 | {
15 | // see https://github.com/NancyFx/Nancy/issues/1154
16 | var track = ((string)parameters.track).Replace("해시", "#");
17 | var trackHashtags = (await hashtags.GetAsync(track)).ToList();
18 | var entries = trackHashtags
19 | .Where(item => !ignoredHashtagsService.Get().Contains(item.Text, StringComparer.OrdinalIgnoreCase))
20 | .GroupBy(tweet => tweet.Text, StringComparer.InvariantCultureIgnoreCase)
21 | .Select(g => new HashtagEntry
22 | {
23 | Text = g.Key,
24 | Count = g.Count(),
25 | })
26 | .OrderByDescending(entry => entry.Count)
27 | .Select((entry, index) =>
28 | {
29 | entry.Position = index + 1;
30 | return entry;
31 | })
32 | .Take(10)
33 | .ToList();
34 |
35 | return new Leaderboard
36 | {
37 | Entries = entries,
38 | Count = trackHashtags.Count,
39 | Since = trackHashtags.Any() ? trackHashtags.Min(hashtag => hashtag.HashtaggedAt) : (DateTime?)null,
40 | LastActivityDateTime = trackHashtags.Any() ? trackHashtags.Max(hashtag => hashtag.HashtaggedAt) : (DateTime?)null,
41 | };
42 | };
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/MostMentionedModule.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using HashBus.ReadModel;
7 | using Nancy;
8 |
9 | public class MostMentionedModule : NancyModule
10 | {
11 | public MostMentionedModule(
12 | IRepository> mentions, IIgnoredUserNamesService ignoredUserNamesService)
13 | {
14 | this.Get["/most-mentioned/{track}", true] = async (parameters, __) =>
15 | {
16 | // see https://github.com/NancyFx/Nancy/issues/1154
17 | var track = ((string)parameters.track).Replace("해시", "#");
18 | var trackMentions = (await mentions.GetAsync(track)).ToList();
19 | var entries = trackMentions
20 | .Where(item => !ignoredUserNamesService.Get().Contains(item.UserMentionScreenName))
21 | .GroupBy(mention => mention.UserMentionId)
22 | .Select(g => new UserEntry
23 | {
24 | Id = g.Key,
25 | Name = g.First().UserMentionName,
26 | ScreenName = g.First().UserMentionScreenName,
27 | Count = g.Count(),
28 | })
29 | .OrderByDescending(entry => entry.Count)
30 | .Select((entry, index) =>
31 | {
32 | entry.Position = index + 1;
33 | return entry;
34 | })
35 | .Take(10)
36 | .ToList();
37 |
38 | return new Leaderboard
39 | {
40 | Entries = entries,
41 | Count = trackMentions.Count,
42 | Since = trackMentions.Any() ? trackMentions.Min(mention => mention.MentionedAt) : (DateTime?) null,
43 | LastActivityDateTime = trackMentions.Any()? trackMentions.Max(mention => mention.MentionedAt): (DateTime?) null,
44 | };
45 | };
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/MostRetweetedModule.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using HashBus.ReadModel;
7 | using Nancy;
8 |
9 | public class MostRetweetedModule : NancyModule
10 | {
11 | public MostRetweetedModule(
12 | IRepository> tweets, IIgnoredUserNamesService ignoredUserNamesService)
13 | {
14 | this.Get["/most-retweeted/{track}", true] = async (parameters, __) =>
15 | {
16 | // see https://github.com/NancyFx/Nancy/issues/1154
17 | var track = ((string)parameters.track).Replace("해시", "#");
18 | var trackTweets = (await tweets.GetAsync(track)).ToList();
19 | var entries = trackTweets
20 | .Where(item => !ignoredUserNamesService.Get().Contains(item.UserScreenName))
21 | .GroupBy(tweet => tweet.UserId)
22 | .Select(g => new UserEntry
23 | {
24 | Id = g.Key,
25 | Name = g.First().UserName,
26 | ScreenName = g.First().UserScreenName,
27 | Count = g.Count(),
28 | })
29 | .OrderByDescending(entry => entry.Count)
30 | .Select((entry, index) =>
31 | {
32 | entry.Position = index + 1;
33 | return entry;
34 | })
35 | .Take(10)
36 | .ToList();
37 |
38 | return new Leaderboard
39 | {
40 | Entries = entries,
41 | Count = trackTweets.Count,
42 | Since = trackTweets.Any() ? trackTweets.Min(tweet => tweet.RetweetedAt) : (DateTime?)null,
43 | LastActivityDateTime = trackTweets.Any() ? trackTweets.Max(tweet => tweet.RetweetedAt) : (DateTime?)null,
44 | };
45 | };
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/Program.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System;
4 | using System.Configuration;
5 | using System.Linq;
6 |
7 | class Program
8 | {
9 | static void Main()
10 | {
11 | var baseUri = new Uri(ConfigurationManager.AppSettings["BaseUri"]);
12 | var mongoConnectionString = ConfigurationManager.AppSettings["MongoConnectionString"];
13 | var mongoDBDatabase = ConfigurationManager.AppSettings["MongoDBDatabase"];
14 | var ignoredUserNames = ConfigurationManager.AppSettings["IgnoredUserNames"]
15 | .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
16 | .Select(userName => userName.Trim());
17 |
18 | var ignoredHashtags = ConfigurationManager.AppSettings["IgnoredHashtags"]
19 | .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
20 | .Select(hashtag => hashtag.Trim());
21 |
22 | Console.Title = typeof(Program).Assembly.GetName().Name;
23 |
24 | App.Run(baseUri, mongoConnectionString, mongoDBDatabase, ignoredUserNames, ignoredHashtags);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/TopRetweetersModule.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using HashBus.ReadModel;
7 | using Nancy;
8 |
9 | public class TopRetweetersModule : NancyModule
10 | {
11 | public TopRetweetersModule
12 | (IRepository> tweets, IIgnoredUserNamesService ignoredUserNamesService)
13 | {
14 | this.Get["/top-retweeters/{track}", true] = async (parameters, __) =>
15 | {
16 | // see https://github.com/NancyFx/Nancy/issues/1154
17 | var track = ((string)parameters.track).Replace("해시", "#");
18 | var trackTweets = (await tweets.GetAsync(track)).ToList();
19 | var entries = trackTweets
20 | .Where(item => !ignoredUserNamesService.Get().Contains(item.UserScreenName))
21 | .GroupBy(tweet => tweet.UserId)
22 | .Select(g => new UserEntry
23 | {
24 | Id = g.Key,
25 | Name = g.First().UserName,
26 | ScreenName = g.First().UserScreenName,
27 | Count = g.Count(),
28 | })
29 | .OrderByDescending(entry => entry.Count)
30 | .Select((entry, index) =>
31 | {
32 | entry.Position = index + 1;
33 | return entry;
34 | })
35 | .Take(10)
36 | .ToList();
37 |
38 | return new Leaderboard
39 | {
40 | Entries = entries,
41 | Count = trackTweets.Count,
42 | Since = trackTweets.Any() ? trackTweets.Min(tweet => tweet.RetweetedAt) : (DateTime?)null,
43 | LastActivityDateTime = trackTweets.Any() ? trackTweets.Max(tweet => tweet.RetweetedAt) : (DateTime?)null,
44 | };
45 | };
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/TopTweetersModule.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using HashBus.ReadModel;
7 | using Nancy;
8 |
9 | public class TopTweetersModule : NancyModule
10 | {
11 | public TopTweetersModule(
12 | IRepository> tweets, IIgnoredUserNamesService ignoredUserNamesService)
13 | {
14 | this.Get["/top-tweeters/{track}", true] = async (parameters, __) =>
15 | {
16 | // see https://github.com/NancyFx/Nancy/issues/1154
17 | var track = ((string)parameters.track).Replace("해시", "#");
18 | var trackTweets = (await tweets.GetAsync(track)).ToList();
19 | var entries = trackTweets
20 | .Where(item => !ignoredUserNamesService.Get().Contains(item.UserScreenName))
21 | .GroupBy(tweet => tweet.UserId)
22 | .Select(g => new UserEntry
23 | {
24 | Id = g.Key,
25 | Name = g.First().UserName,
26 | ScreenName = g.First().UserScreenName,
27 | Count = g.Count(),
28 | })
29 | .OrderByDescending(entry => entry.Count)
30 | .Select((entry, index) =>
31 | {
32 | entry.Position = index + 1;
33 | return entry;
34 | })
35 | .Take(10)
36 | .ToList();
37 |
38 | return new Leaderboard
39 | {
40 | Entries = entries,
41 | Count = trackTweets.Count,
42 | Since = trackTweets.Any() ? trackTweets.Min(tweet => tweet.TweetedAt) : (DateTime?)null,
43 | LastActivityDateTime = trackTweets.Any() ? trackTweets.Max(tweet => tweet.TweetedAt) : (DateTime?)null,
44 | };
45 | };
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/TopTweetersRetweetersModule.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using HashBus.ReadModel;
7 | using Nancy;
8 |
9 | public class TopTweetersRetweetersModule : NancyModule
10 | {
11 | public TopTweetersRetweetersModule(
12 | IRepository> tweets, IIgnoredUserNamesService ignoredUserNamesService)
13 | {
14 | this.Get["/top-tweeters-retweeters/{track}", true] = async (parameters, __) =>
15 | {
16 | // see https://github.com/NancyFx/Nancy/issues/1154
17 | var track = ((string)parameters.track).Replace("해시", "#");
18 | var trackTweets = (await tweets.GetAsync(track)).ToList();
19 | var entries = trackTweets
20 | .Where(item => !ignoredUserNamesService.Get().Contains(item.UserScreenName))
21 | .GroupBy(tweet => tweet.UserId)
22 | .Select(g => new UserEntry
23 | {
24 | Id = g.Key,
25 | Name = g.First().UserName,
26 | ScreenName = g.First().UserScreenName,
27 | Count = g.Count(),
28 | })
29 | .OrderByDescending(entry => entry.Count)
30 | .Select((entry, index) =>
31 | {
32 | entry.Position = index + 1;
33 | return entry;
34 | })
35 | .Take(10)
36 | .ToList();
37 |
38 | return new Leaderboard
39 | {
40 | Entries = entries,
41 | Count = trackTweets.Count,
42 | Since = trackTweets.Any() ? trackTweets.Min(tweet => tweet.TweetedRetweetedAt) : (DateTime?)null,
43 | LastActivityDateTime = trackTweets.Any() ? trackTweets.Max(tweet => tweet.TweetedRetweetedAt) : (DateTime?)null,
44 | };
45 | };
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/UserEntry.cs:
--------------------------------------------------------------------------------
1 | namespace HashBus.WebApi
2 | {
3 | public class UserEntry : IEntry
4 | {
5 | public int Position { get; set; }
6 |
7 | public long? Id { get; set; }
8 |
9 | public string Name { get; set; }
10 |
11 | public string ScreenName { get; set; }
12 |
13 | public int Count { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/HashBus.WebApi/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------