├── .gitignore ├── README.md └── ThemeBot ├── ThemeBot.sln └── ThemeBot ├── Access.cs ├── App.config ├── Download.cs ├── LUser.cs ├── Program.cs ├── Properties └── AssemblyInfo.cs ├── QuestionType.cs ├── Rating.cs ├── Theme.cs ├── ThemeBot.csproj ├── ThemeModel.Context.cs ├── ThemeModel.Context.tt ├── ThemeModel.Designer.cs ├── ThemeModel.cs ├── ThemeModel.edmx ├── ThemeModel.edmx.diagram ├── ThemeModel.tt ├── User.cs └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | #Internal settings (passwords, api keys, etc) 11 | Interal.settings 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | [Xx]64/ 22 | [Xx]86/ 23 | [Bb]uild/ 24 | bld/ 25 | [Bb]in/ 26 | [Oo]bj/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | artifacts/ 49 | 50 | *_i.c 51 | *_p.c 52 | *_i.h 53 | *.ilk 54 | *.meta 55 | *.obj 56 | *.pch 57 | *.pdb 58 | *.pgc 59 | *.pgd 60 | *.rsp 61 | *.sbr 62 | *.tlb 63 | *.tli 64 | *.tlh 65 | *.tmp 66 | *.tmp_proj 67 | *.log 68 | *.vspscc 69 | *.vssscc 70 | .builds 71 | *.pidb 72 | *.svclog 73 | *.scc 74 | 75 | # Chutzpah Test files 76 | _Chutzpah* 77 | 78 | # Visual C++ cache files 79 | ipch/ 80 | *.aps 81 | *.ncb 82 | *.opendb 83 | *.opensdf 84 | *.sdf 85 | *.cachefile 86 | *.VC.db 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | *.sap 93 | 94 | # TFS 2012 Local Workspace 95 | $tf/ 96 | 97 | # Guidance Automation Toolkit 98 | *.gpState 99 | 100 | # ReSharper is a .NET coding add-in 101 | _ReSharper*/ 102 | *.[Rr]e[Ss]harper 103 | *.DotSettings.user 104 | 105 | # JustCode is a .NET coding add-in 106 | .JustCode 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # NCrunch 115 | _NCrunch_* 116 | .*crunch*.local.xml 117 | nCrunchTemp_* 118 | 119 | # MightyMoose 120 | *.mm.* 121 | AutoTest.Net/ 122 | 123 | # Web workbench (sass) 124 | .sass-cache/ 125 | 126 | # Installshield output folder 127 | [Ee]xpress/ 128 | 129 | # DocProject is a documentation generator add-in 130 | DocProject/buildhelp/ 131 | DocProject/Help/*.HxT 132 | DocProject/Help/*.HxC 133 | DocProject/Help/*.hhc 134 | DocProject/Help/*.hhk 135 | DocProject/Help/*.hhp 136 | DocProject/Help/Html2 137 | DocProject/Help/html 138 | 139 | # Click-Once directory 140 | publish/ 141 | 142 | # Publish Web Output 143 | *.[Pp]ublish.xml 144 | *.azurePubxml 145 | 146 | # TODO: Un-comment the next line if you do not want to checkin 147 | # your web deploy settings because they may include unencrypted 148 | # passwords 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # NuGet Packages 153 | *.nupkg 154 | # The packages folder can be ignored because of Package Restore 155 | **/packages/* 156 | # except build/, which is used as an MSBuild target. 157 | !**/packages/build/ 158 | # Uncomment if necessary however generally it will be regenerated when needed 159 | #!**/packages/repositories.config 160 | # NuGet v3's project.json files produces more ignoreable files 161 | *.nuget.props 162 | *.nuget.targets 163 | 164 | # Microsoft Azure Build Output 165 | csx/ 166 | *.build.csdef 167 | 168 | # Microsoft Azure Emulator 169 | ecf/ 170 | rcf/ 171 | 172 | # Microsoft Azure ApplicationInsights config file 173 | ApplicationInsights.config 174 | 175 | # Windows Store app package directory 176 | AppPackages/ 177 | BundleArtifacts/ 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | [Ss]tyle[Cc]op.* 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.pfx 193 | *.publishsettings 194 | node_modules/ 195 | orleans.codegen.cs 196 | 197 | # RIA/Silverlight projects 198 | Generated_Code/ 199 | 200 | # Backup & report files from converting an old project file 201 | # to a newer Visual Studio version. Backup files are not needed, 202 | # because we have git ;-) 203 | _UpgradeReport_Files/ 204 | Backup*/ 205 | UpgradeLog*.XML 206 | UpgradeLog*.htm 207 | 208 | # SQL Server files 209 | *.mdf 210 | *.ldf 211 | 212 | # Business Intelligence projects 213 | *.rdl.data 214 | *.bim.layout 215 | *.bim_*.settings 216 | 217 | # Microsoft Fakes 218 | FakesAssemblies/ 219 | 220 | # GhostDoc plugin setting file 221 | *.GhostDoc.xml 222 | 223 | # Node.js Tools for Visual Studio 224 | .ntvs_analysis.dat 225 | 226 | # Visual Studio 6 build log 227 | *.plg 228 | 229 | # Visual Studio 6 workspace options file 230 | *.opt 231 | 232 | # Visual Studio LightSwitch build output 233 | **/*.HTMLClient/GeneratedArtifacts 234 | **/*.DesktopClient/GeneratedArtifacts 235 | **/*.DesktopClient/ModelManifest.xml 236 | **/*.Server/GeneratedArtifacts 237 | **/*.Server/ModelManifest.xml 238 | _Pvt_Extensions 239 | 240 | # LightSwitch generated files 241 | GeneratedArtifacts/ 242 | ModelManifest.xml 243 | 244 | # Paket dependency manager 245 | .paket/paket.exe 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | **/Internal.settings 250 | **/Internal.Designer.cs 251 | # drop cert 252 | *.crt 253 | /Werewolf for Telegram/Werewolf Control/Languages 254 | /Werewolf for Telegram/Werewolf Node/Languages 255 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Telegram Theme Bot 2 | ## Currently [@tthemebot](https://t.me/tthemebot) 3 | Use this bot to upload your TDesktop themes (available for Telegram Desktop builds since v1.0), manage them, or search for themes inline (i.e. type `@tthemebot `) 4 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThemeBot", "ThemeBot\ThemeBot.csproj", "{421A17A6-C57D-45A4-B37D-985232E3A86D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {421A17A6-C57D-45A4-B37D-985232E3A86D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {421A17A6-C57D-45A4-B37D-985232E3A86D}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {421A17A6-C57D-45A4-B37D-985232E3A86D}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {421A17A6-C57D-45A4-B37D-985232E3A86D}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/Access.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ThemeBot 8 | { 9 | [Flags] 10 | public enum Access 11 | { 12 | Standard = 0, 13 | AutoApprove = 1, 14 | Moderator = 2, 15 | Admin = 4, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/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 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/Download.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace ThemeBot 11 | { 12 | using System; 13 | using System.Collections.Generic; 14 | 15 | public partial class Download 16 | { 17 | public int Id { get; set; } 18 | public int UserID { get; set; } 19 | public int ThemeID { get; set; } 20 | public System.DateTime TimeStamp { get; set; } 21 | 22 | public virtual Theme Theme { get; set; } 23 | public virtual User User { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/LUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ThemeBot 8 | { 9 | public class LUser 10 | { 11 | /// 12 | /// Users Telegram ID 13 | /// 14 | public int Id { get; set; } 15 | /// 16 | /// What question did we ask the user 17 | /// 18 | public QuestionType QuestionAsked { get; set; } = QuestionType.None; 19 | /// 20 | /// Results of their last search 21 | /// 22 | public List ResultSet { get; set; } = new List(); 23 | 24 | /// 25 | /// What page of the results are they on? 26 | /// 27 | public int Page { get; set; } = 0; 28 | public Theme ThemeCreating { get; set; } 29 | public string Search { get; set; } 30 | public Theme ThemeUpdating { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Data.Entity.Validation; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Net.Mail; 8 | using System.Reflection; 9 | using System.Security.Cryptography.X509Certificates; 10 | using System.Text; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | using Ionic.Zip; 14 | using Ionic.Zlib; 15 | using Microsoft.Win32; 16 | using Telegram.Bot; 17 | using Telegram.Bot.Args; 18 | using Telegram.Bot.Types; 19 | using Telegram.Bot.Types.Enums; 20 | using Telegram.Bot.Types.InlineQueryResults; 21 | using Telegram.Bot.Types.InputMessageContents; 22 | using Telegram.Bot.Types.ReplyMarkups; 23 | using File = System.IO.File; 24 | 25 | namespace ThemeBot 26 | { 27 | class Program 28 | { 29 | public static TelegramBotClient Client; 30 | public static List LocalUsers = new List(); 31 | internal static List> CachedThemes = new List>(); 32 | internal static List LoadedThemes = new List(); 33 | const long ThemeGroup = -191419236; 34 | internal static string RootDirectory 35 | { 36 | get 37 | { 38 | string codeBase = Assembly.GetExecutingAssembly().CodeBase; 39 | UriBuilder uri = new UriBuilder(codeBase); 40 | string path = Uri.UnescapeDataString(uri.Path); 41 | return Path.GetDirectoryName(path); 42 | } 43 | } 44 | 45 | internal static string ThemesDirectory = Path.GetFullPath(Path.Combine(RootDirectory, "Themes")); 46 | 47 | static void Main(string[] args) 48 | { 49 | new Thread(InitializeBot).Start(); 50 | Thread.Sleep(-1); 51 | } 52 | 53 | private static void InitializeBot() 54 | { 55 | var api = 56 | #if DEBUG 57 | "DebugAPI"; 58 | #else 59 | "ProductionAPI"; 60 | #endif 61 | Client = new TelegramBotClient(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey("SOFTWARE\\ThemeBot").GetValue(api).ToString()); 62 | Client.OnMessage += ClientOnOnMessage; 63 | Client.OnCallbackQuery += ClientOnOnCallbackQuery; 64 | Client.OnInlineQuery += ClientOnOnInlineQuery; 65 | Client.OnInlineResultChosen += ClientOnOnInlineResultChosen; 66 | Client.StartReceiving(); 67 | CacheThemes(); 68 | } 69 | 70 | private static void ClientOnOnInlineResultChosen(object sender, ChosenInlineResultEventArgs chosenInlineResultEventArgs) 71 | { 72 | 73 | } 74 | 75 | private static void ClientOnOnInlineQuery(object sender, InlineQueryEventArgs inlineQueryEventArgs) 76 | { 77 | try 78 | { 79 | var q = inlineQueryEventArgs.InlineQuery; 80 | CreateOrUpdateDBUser(q.From); 81 | var lu = GetLocalUser(q.From.Id); 82 | var search = q.Query.ToLower(); 83 | if (lu.Search != search) 84 | { 85 | lu.Search = search; 86 | lu.Page = 0; 87 | using (var db = new tdthemeEntities()) 88 | { 89 | lu.ResultSet = LoadedThemes.ToList().Where(x => (x.Description.ToLower().Contains(search) || x.Name.ToLower().Contains(search))).ToList(); 90 | } 91 | if (!lu.ResultSet.Any()) 92 | { 93 | Client.AnswerInlineQueryAsync(q.Id, 94 | new InlineQueryResult[0]); 95 | } 96 | } 97 | var offset = 0; 98 | //display results - 5 at a time 99 | if (!String.IsNullOrEmpty(q.Offset)) 100 | offset = int.Parse(q.Offset); 101 | 102 | var toSend = 103 | lu.ResultSet.Skip(offset) 104 | .Take(5) 105 | .Select( 106 | #if !DEBUG 107 | x => new InlineQueryResultCachedPhoto 108 | { 109 | Description = x.Description, 110 | Caption = $"{x.Name}\n{x.Description}" + (x.ShowOwnerName ? $"\nBy {x.User.Name}" + (x.ShowOwnerUsername ? $" (@{x.User.Username})" : "") : (x.ShowOwnerUsername ? $"\nBy @{x.User.Username}" : "")), 111 | FileId = x.Photo_Id, 112 | Id = x.Id.ToString(), 113 | //InputMessageContent = new InputTextMessageContent { MessageText = x.Description + "text", DisableWebPagePreview = true }, 114 | Title = x.Name, 115 | ReplyMarkup = new InlineKeyboardMarkup(new[] { new InlineKeyboardButton("Get Theme") { Url = "https://t.me/tthemebot?start=t" + x.Id }, new InlineKeyboardButton("Rate") { Url = "https://t.me/tthemebot?start=r" + x.Id } }) 116 | }).ToArray(); 117 | #else 118 | x => new InlineQueryResultArticle() 119 | { 120 | Description = x.Description, 121 | Id = x.Id.ToString(), 122 | InputMessageContent = new InputTextMessageContent { MessageText = $"{x.Name}\n{x.Description}" + (x.ShowOwnerName ? $"\nBy {x.User.Name}" + (x.ShowOwnerUsername ? $" (@{x.User.Username})" : "") : ""), DisableWebPagePreview = true }, 123 | Title = x.Name, 124 | ReplyMarkup = new InlineKeyboardMarkup(new[] { new InlineKeyboardButton("Get Theme") { Url = "https://t.me/tthemebot?start=t" + x.Id }, new InlineKeyboardButton("Rate") { Url = "https://t.me/tthemebot?start=r" + x.Id }, }) 125 | }).ToArray(); 126 | #endif 127 | offset += 5; 128 | var result = Client.AnswerInlineQueryAsync(q.Id, toSend, nextOffset: offset.ToString()).Result; 129 | } 130 | catch (AggregateException e) 131 | { 132 | Client.SendTextMessageAsync(129046388, e.InnerExceptions[0].Message); 133 | } 134 | catch (Exception e) 135 | { 136 | while (e.InnerException != null) 137 | e = e.InnerException; 138 | Client.SendTextMessageAsync(129046388, e.Message + "\n" + e.StackTrace); 139 | } 140 | } 141 | 142 | private static void ClientOnOnCallbackQuery(object sender, CallbackQueryEventArgs callbackQueryEventArgs) 143 | { 144 | try 145 | { 146 | var q = callbackQueryEventArgs.CallbackQuery; 147 | var lu = GetLocalUser(q.From.Id); 148 | Theme t; 149 | if (lu.QuestionAsked == QuestionType.ShowOwnerName && q.Data.StartsWith("showname")) 150 | { 151 | if (lu.ThemeCreating != null) 152 | { 153 | lu.ThemeCreating.ShowOwnerName = q.Data.Split('|')[1] == "yes"; 154 | if (!String.IsNullOrEmpty(q.From.Username)) 155 | { 156 | lu.QuestionAsked = QuestionType.ShowOwnerUsername; 157 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0, default(CancellationToken)); 158 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, 159 | $"Ok, do you want us to show your username? (@{q.From.Username})", replyMarkup: 160 | new InlineKeyboardMarkup(new[] 161 | { 162 | new InlineKeyboardButton("Yes", "showuser|yes"), 163 | new InlineKeyboardButton("No", "showuser|no") 164 | })); 165 | } 166 | else 167 | { 168 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0, default(CancellationToken)); 169 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, 170 | "Ok, give me just a moment to upload your file...", replyMarkup: null); 171 | lu.ThemeCreating.ShowOwnerUsername = false; 172 | SaveTheme(lu); 173 | } 174 | } 175 | else 176 | { 177 | lu.ThemeUpdating.ShowOwnerName = q.Data.Split('|')[1] == "yes"; 178 | if (!String.IsNullOrEmpty(q.From.Username)) 179 | { 180 | lu.QuestionAsked = QuestionType.ShowOwnerUsername; 181 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0, default(CancellationToken)); 182 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, 183 | $"Ok, do you also want us to show your username? (@{q.From.Username})", replyMarkup: 184 | new InlineKeyboardMarkup(new[] 185 | { 186 | new InlineKeyboardButton("Yes", "showuser|yes"), 187 | new InlineKeyboardButton("No", "showuser|no") 188 | })); 189 | } 190 | else 191 | { 192 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0, default(CancellationToken)); 193 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, 194 | "Ok, give me just a moment to upload your file...", replyMarkup: null); 195 | lu.ThemeUpdating.ShowOwnerUsername = false; 196 | SaveTheme(lu, true); 197 | } 198 | 199 | } 200 | return; 201 | } 202 | if (lu.QuestionAsked == QuestionType.ShowOwnerUsername && q.Data.StartsWith("showuser")) 203 | { 204 | if (lu.ThemeCreating != null) 205 | lu.ThemeCreating.ShowOwnerUsername = q.Data.Split('|')[1] == "yes"; 206 | else 207 | lu.ThemeUpdating.ShowOwnerUsername = q.Data.Split('|')[1] == "yes"; 208 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0, default(CancellationToken)); 209 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, 210 | "Ok, give me just a moment to upload your file...", replyMarkup: null); 211 | SaveTheme(lu, lu.ThemeCreating == null); 212 | return; 213 | } 214 | var args = q.Data.Split('|'); 215 | switch (args[0]) 216 | { 217 | //approval 218 | case "app": 219 | var app = args[2] == "yes"; 220 | using (var db = new tdthemeEntities()) 221 | { 222 | var th = db.Themes.FirstOrDefault(x => x.Id == int.Parse(args[1])); 223 | if (th == null) 224 | { 225 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0); 226 | Client.EditMessageCaptionAsync(q.Message.Chat.Id, q.Message.MessageId, $"This theme appears to have been removed already."); 227 | return; 228 | } 229 | th.Approved = app; 230 | if (app) 231 | { 232 | Client.AnswerCallbackQueryAsync(q.Id, "Approved", false, null, 0); 233 | Client.EditMessageCaptionAsync(q.Message.Chat.Id, q.Message.MessageId, $"Approved by: {q.From.FirstName}\n{q.Message.Caption}", 234 | null); 235 | db.SaveChanges(); 236 | } 237 | else 238 | { 239 | //ask for a reason 240 | var menu = new InlineKeyboardMarkup(new[] 241 | { 242 | new[] 243 | { 244 | new InlineKeyboardButton("Image", $"dis|{args[1]}|img"), 245 | new InlineKeyboardButton("Title / Description", $"dis|{args[1]}|text"), 246 | }, 247 | new[] 248 | { 249 | new InlineKeyboardButton("Image and Title", $"dis|{args[1]}|imgtext"), 250 | new InlineKeyboardButton("Other", $"dis|{args[1]}|other"), 251 | } 252 | }); 253 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0); 254 | Client.EditMessageCaptionAsync(q.Message.Chat.Id, q.Message.MessageId, $"{q.Message.Caption}\n\nPlease choose why it is disapproved:", 255 | menu); 256 | } 257 | } 258 | 259 | break; 260 | case "dis": 261 | using (var db = new tdthemeEntities()) 262 | { 263 | var th = db.Themes.FirstOrDefault(x => x.Id == int.Parse(args[1])); 264 | if (th == null) 265 | { 266 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0); 267 | Client.EditMessageCaptionAsync(q.Message.Chat.Id, q.Message.MessageId, $"This theme appears to have been removed already."); 268 | return; 269 | } 270 | string msg = "Your theme was not approved\n"; 271 | var send = true; 272 | switch (args[2]) 273 | { 274 | case "img": 275 | msg += "Please use the official Telegram theme preview screenshot\n"; 276 | break; 277 | case "text": 278 | msg += "Please update the Name / Description with something more descriptive / meaningful\n"; 279 | break; 280 | case "imgtext": 281 | msg += "Please use the official Telegram theme preview screenshot\n"; 282 | msg += "Please update the Name / Description with something more descriptive / meaningful\n"; 283 | break; 284 | case "other": 285 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0); 286 | Client.EditMessageCaptionAsync(q.Message.Chat.Id, q.Message.MessageId, $"{q.Message.Caption}\n\nPlease use the manual disapproval command", 287 | null); 288 | send = false; 289 | break; 290 | } 291 | 292 | if (send) 293 | { 294 | th.Approved = false; 295 | db.SaveChanges(); 296 | msg += "Use /edittheme to update your submission"; 297 | Client.SendTextMessageAsync(th.User.TelegramID, msg); 298 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0); 299 | Client.EditMessageCaptionAsync(q.Message.Chat.Id, q.Message.MessageId, $"{q.Message.Caption}\n\nMessage sent to user", 300 | null); 301 | } 302 | } 303 | 304 | break; 305 | case "update": 306 | using (var db = new tdthemeEntities()) 307 | { 308 | t = db.Themes.Find(int.Parse(args[1])); 309 | lu.ThemeUpdating = t; 310 | //do newtheme tasks 311 | lu.QuestionAsked = QuestionType.ThemeName; 312 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0); 313 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, 314 | "Updating theme. Please enter a new name, or hit /keep to keep the same name:\n" + 315 | t.Name); 316 | } 317 | break; 318 | case "delete": 319 | using (var db = new tdthemeEntities()) 320 | { 321 | t = db.Themes.Find(int.Parse(args[1])); 322 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0); 323 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, 324 | $"Are you sure you want to delete {t.Name}?", replyMarkup: 325 | new InlineKeyboardMarkup(new[] 326 | { 327 | new InlineKeyboardButton("Yes", $"confirm|{args[1]}"), 328 | new InlineKeyboardButton("Cancel", "confirm|no") 329 | })); 330 | } 331 | break; 332 | case "confirm": 333 | if (args[1] != "no") 334 | { 335 | using (var db = new tdthemeEntities()) 336 | { 337 | try 338 | { 339 | t = db.Themes.Find(int.Parse(args[1])); 340 | foreach (var r in t.Ratings.ToList()) 341 | db.Ratings.Remove(r); 342 | foreach (var d in t.Downloads.ToList()) 343 | db.Downloads.Remove(d); 344 | db.Themes.Remove(t); 345 | db.SaveChanges(); 346 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0); 347 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, 348 | $"{t.Name} has been deleted."); 349 | } 350 | catch (Exception e) 351 | { 352 | Client.SendTextMessageAsync(q.From.Id, 353 | $"Unable to delete theme: " + e.Message + "\nPlease try again"); 354 | } 355 | } 356 | } 357 | else 358 | { 359 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0); 360 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, "Cancelled delete operation"); 361 | } 362 | break; 363 | case "rate": 364 | Client.AnswerCallbackQueryAsync(q.Id, null, false, null, 0); 365 | if (args[1] == "no") 366 | { 367 | #if !DEBUG 368 | Client.EditMessageCaptionAsync(q.From.Id, q.Message.MessageId, "Enjoy your theme!"); 369 | #else 370 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, "Enjoy your theme!"); 371 | #endif 372 | } 373 | else 374 | { 375 | using (var db = new tdthemeEntities()) 376 | { 377 | var themeId = args[1]; 378 | var rTheme = db.Themes.FirstOrDefault(x => x.Id.ToString() == themeId); 379 | var rUser = db.Users.FirstOrDefault(x => x.TelegramID == q.From.Id); 380 | var rating = db.Ratings.FirstOrDefault(x => x.UserId == rUser.Id && x.ThemeId == rTheme.Id); 381 | if (rating == null) 382 | { 383 | rating = new Rating 384 | { 385 | ThemeId = rTheme.Id, 386 | UserId = rUser.Id, 387 | TimeStamp = DateTime.UtcNow, 388 | Rating1 = int.Parse(args[2]) 389 | }; 390 | db.Ratings.Add(rating); 391 | } 392 | else 393 | rating.Rating1 = int.Parse(args[2]); 394 | db.SaveChanges(); 395 | } 396 | #if !DEBUG 397 | Client.EditMessageCaptionAsync(q.From.Id, q.Message.MessageId, "Thank you for rating!"); 398 | #else 399 | Client.EditMessageTextAsync(q.From.Id, q.Message.MessageId, "Thank you for rating!"); 400 | #endif 401 | } 402 | break; 403 | 404 | } 405 | 406 | 407 | } 408 | catch (AggregateException e) 409 | { 410 | Client.SendTextMessageAsync(ThemeGroup, e.InnerExceptions[0].Message); 411 | } 412 | catch (Exception e) 413 | { 414 | while (e.InnerException != null) 415 | e = e.InnerException; 416 | Client.SendTextMessageAsync(ThemeGroup, e.Message + "\n" + e.StackTrace); 417 | } 418 | } 419 | 420 | private static void SaveTheme(LUser lu, bool update = false) 421 | { 422 | try 423 | { 424 | using (var db = new tdthemeEntities()) 425 | { 426 | if (!update) 427 | { 428 | //get the database user 429 | var thisUser = db.Users.FirstOrDefault(x => x.TelegramID == lu.Id); 430 | if (thisUser.AccessFlags == null) 431 | thisUser.AccessFlags = 0; 432 | var flags = (Access)thisUser.AccessFlags; 433 | if (flags.HasFlag(Access.AutoApprove)) 434 | lu.ThemeCreating.Approved = true; 435 | else 436 | lu.ThemeCreating.Approved = null; 437 | lu.ThemeCreating.LastUpdated = DateTime.UtcNow; 438 | thisUser.Themes.Add(lu.ThemeCreating); 439 | db.SaveChanges(); 440 | 441 | if (flags.HasFlag(Access.AutoApprove)) 442 | { 443 | Client.SendTextMessageAsync(lu.Id, "Your theme is ready!"); 444 | CacheThemes(); 445 | } 446 | else 447 | { 448 | //theme is awaiting approval, PM Para 449 | RequestApproval(lu.ThemeCreating.Id); 450 | 451 | Client.SendTextMessageAsync(lu.Id, 452 | "Your theme has been uploaded, and is awaiting approval from a moderator"); 453 | } 454 | } 455 | else 456 | { 457 | var t = db.Themes.FirstOrDefault(x => x.Id == lu.ThemeUpdating.Id); 458 | var send = t.Approved != true; 459 | t.Approved = t.Approved == false ? null : t.Approved; 460 | var th = lu.ThemeUpdating; 461 | t.Description = th.Description; 462 | t.FileName = th.FileName; 463 | t.File_Id = th.File_Id; 464 | t.Name = th.Name; 465 | t.Photo_Id = th.Photo_Id; 466 | t.ShowOwnerName = th.ShowOwnerName; 467 | t.ShowOwnerUsername = th.ShowOwnerUsername; 468 | t.LastUpdated = DateTime.UtcNow; 469 | db.SaveChanges(); 470 | if (send) 471 | { 472 | Client.SendTextMessageAsync(lu.Id, "Your theme is pending approval."); 473 | RequestApproval(t.Id); 474 | } 475 | else 476 | { 477 | CacheThemes(); 478 | Client.SendTextMessageAsync(lu.Id, "Your theme is ready!"); 479 | } 480 | 481 | } 482 | 483 | 484 | LocalUsers.Remove(lu); 485 | lu = null; 486 | } 487 | } 488 | catch (AggregateException e) 489 | { 490 | Client.SendTextMessageAsync(129046388, e.InnerExceptions[0].Message); 491 | } 492 | catch (Exception e) 493 | { 494 | while (e.InnerException != null) 495 | e = e.InnerException; 496 | Client.SendTextMessageAsync(129046388, e.Message + "\n" + e.StackTrace); 497 | } 498 | } 499 | 500 | private static void RequestApproval(int id) 501 | { 502 | using (var db = new tdthemeEntities()) 503 | { 504 | var t = db.Themes.FirstOrDefault(x => x.Id == id); 505 | //create menu 506 | var menu = new InlineKeyboardMarkup(new[] 507 | { 508 | new[] 509 | { 510 | new InlineKeyboardButton("Approve", $"app|{id}|yes"), 511 | new InlineKeyboardButton("Disapprove", $"app|{id}|no"), 512 | } 513 | }); 514 | Client.SendPhotoAsync(ThemeGroup, t.Photo_Id, 515 | $"Theme pending approval:\n\n{t.Id}\n{t.Name}\n{t.Description}\n{(t.User.Username == null ? t.User.Name : "@" + t.User.Username)}", replyMarkup: menu); 516 | Client.SendDocumentAsync(ThemeGroup, t.File_Id); 517 | } 518 | 519 | 520 | } 521 | 522 | private static async void ClientOnOnMessage(object sender, MessageEventArgs messageEventArgs) 523 | { 524 | new Thread(() => HandleMessage(messageEventArgs.Message)).Start(); 525 | } 526 | 527 | private static void HandleMessage(Message m) 528 | { 529 | try 530 | { 531 | if (m.Date < DateTime.UtcNow.AddSeconds(-30)) return; //ignore old / lagged messages 532 | if (m.Type == MessageType.TextMessage) 533 | { 534 | CreateOrUpdateDBUser(m.From); 535 | if (m.Text.StartsWith("/") || m.Text.StartsWith("!")) 536 | { 537 | LUser lu; 538 | var param = m.Text.Split(' '); 539 | var cmd = param[0].Replace("/", "").Replace("!", ""); 540 | 541 | switch (cmd) 542 | { 543 | case "start": 544 | if (param.Length > 1) 545 | { 546 | //check for start parameter 547 | var arg = param[1]; 548 | if (arg.StartsWith("t")) //get the theme 549 | { 550 | //they want a theme 551 | arg = arg.Substring(1); 552 | using (var db = new tdthemeEntities()) 553 | { 554 | var theme = db.Themes.FirstOrDefault(x => x.Id.ToString() == arg); 555 | var dUser = db.Users.FirstOrDefault(x => x.TelegramID == m.From.Id); 556 | if (theme != null && dUser != null) 557 | { 558 | theme.Downloads.Add(new Download 559 | { 560 | TimeStamp = DateTime.UtcNow, 561 | UserID = dUser.Id 562 | }); 563 | db.SaveChanges(); 564 | } 565 | if (theme != null) 566 | Client.SendDocumentAsync(m.Chat.Id, new FileToSend(theme.File_Id), 567 | theme.Name); 568 | else 569 | Client.SendTextMessageAsync(m.From.Id, 570 | "This theme does not appear to exist anymore."); 571 | 572 | } 573 | break; 574 | } 575 | if (arg.StartsWith("r")) //rate the theme 576 | { 577 | arg = arg.Substring(1); 578 | using (var db = new tdthemeEntities()) 579 | { 580 | var theme = db.Themes.FirstOrDefault(x => x.Id.ToString() == arg); 581 | //send rate menu 582 | #if !DEBUG 583 | var result = 584 | Client.SendPhotoAsync(m.From.Id, theme.Photo_Id, 585 | $"How would you rate {theme.Name}?\n(1 - Did not like at all, 5 - it's awesome!)", 586 | replyMarkup: 587 | new InlineKeyboardMarkup(new[] 588 | { 589 | Enumerable.Range(1, 5) 590 | .Select( 591 | x => 592 | new InlineKeyboardButton(x.ToString(), 593 | $"rate|{theme.Id}|{x}")) 594 | .ToArray(), 595 | new[] 596 | { 597 | new InlineKeyboardButton("No Thanks", "rate|no") 598 | }, 599 | })).Result; 600 | #else 601 | var result = Client.SendTextMessageAsync(m.From.Id, $"How would you rate {theme.Name}?\n(1 - Did not like at all, 5 - it's awesome!)", replyMarkup: 602 | new InlineKeyboardMarkup(new[] { 603 | Enumerable.Range(1, 5).Select(x => new InlineKeyboardButton(x.ToString(), $"rate|{theme.Id}|{x}")).ToArray(), 604 | new [] 605 | { 606 | new InlineKeyboardButton("No Thanks", "rate|no") 607 | }, })).Result; 608 | #endif 609 | } 610 | break; 611 | } 612 | } 613 | if (m.Chat.Type == ChatType.Private) 614 | { 615 | 616 | 617 | Client.SendTextMessageAsync(m.From.Id, 618 | "Welcome to Telegram Themes Search Bot!\nHere you can search for cool themes for Telegram, or upload your own creations for others to use.\n\nAvailable commands:\n" + 619 | "/help - Show this list\n" + 620 | "/newtheme - Upload a new theme to the catalog\n" + 621 | "/edittheme - Edit one of your themes\n" + 622 | "/deletetheme - Delete a theme\n" + 623 | "/cancel - Cancel the current operation\n\n" + 624 | "You can also search for themes inline"); 625 | } 626 | break; 627 | case "help": 628 | 629 | Client.SendTextMessageAsync(m.Chat.Id, "Available commands:\n" + 630 | "/help - Show this list\n" + 631 | "/newtheme - Upload a new theme to the catalog\n" + 632 | "/edittheme - Edit one of your themes\n" + 633 | "/deletetheme - Delete a theme\n" + 634 | "/cancel - Cancel the current operation\n\n" + 635 | "You can also search for themes inline"); 636 | break; 637 | case "newtheme": 638 | if (m.Chat.Type == ChatType.Private) 639 | { 640 | lu = GetLocalUser(m.From.Id); 641 | Client.SendTextMessageAsync(m.From.Id, 642 | "Great! You want to upload a new theme. Let's begin.\nFirst, what is the name of your theme?"); 643 | lu.ThemeCreating = new Theme(); 644 | lu.QuestionAsked = QuestionType.ThemeName; 645 | } 646 | break; 647 | case "edittheme": 648 | lu = GetLocalUser(m.From.Id); 649 | LocalUsers.Remove(lu); 650 | lu = null; 651 | if (m.Chat.Type != ChatType.Private) 652 | { 653 | Client.SendTextMessageAsync(m.Chat.Id, "Please edit your themes in PM"); 654 | break; 655 | } 656 | using (var db = new tdthemeEntities()) 657 | { 658 | var usr = db.Users.FirstOrDefault(x => x.TelegramID == m.From.Id); 659 | if (usr != null) 660 | { 661 | if (usr.Themes.Any()) 662 | { 663 | Client.SendTextMessageAsync(m.From.Id, 664 | "Which theme do you want to update?", 665 | replyMarkup: new InlineKeyboardMarkup( 666 | usr.Themes.Select( 667 | x => new[] { new InlineKeyboardButton(x.Name, $"update|{x.Id}") }) 668 | .ToArray() 669 | )); 670 | break; 671 | } 672 | } 673 | Client.SendTextMessageAsync(m.From.Id, 674 | "You don't have any themes to update! Upload new themes with /newtheme"); 675 | } 676 | break; 677 | case "deletetheme": 678 | if (m.Chat.Type != ChatType.Private) 679 | { 680 | Client.SendTextMessageAsync(m.Chat.Id, "Please delete your themes in PM"); 681 | break; 682 | } 683 | using (var db = new tdthemeEntities()) 684 | { 685 | var usr = db.Users.FirstOrDefault(x => x.TelegramID == m.From.Id); 686 | if (usr != null) 687 | { 688 | if (usr.Themes.Any()) 689 | { 690 | Client.SendTextMessageAsync(m.From.Id, 691 | "Which theme do you want to delete?", 692 | replyMarkup: new InlineKeyboardMarkup( 693 | usr.Themes.Select( 694 | x => new[] { new InlineKeyboardButton(x.Name, $"delete|{x.Id}") }) 695 | .ToArray() 696 | )); 697 | break; 698 | } 699 | } 700 | Client.SendTextMessageAsync(m.From.Id, 701 | "You don't have any themes to update! Upload new themes with /newtheme"); 702 | } 703 | break; 704 | case "cancel": 705 | if (m.Chat.Type == ChatType.Private) 706 | { 707 | lu = GetLocalUser(m.From.Id); 708 | LocalUsers.Remove(lu); 709 | lu = null; 710 | Client.SendTextMessageAsync(m.From.Id, "Operation cancelled"); 711 | } 712 | 713 | break; 714 | case "keep": 715 | lu = GetLocalUser(m.From.Id); 716 | if (lu.ThemeUpdating == null) 717 | { 718 | //ignore 719 | } 720 | else 721 | { 722 | string msg = ""; 723 | InlineKeyboardMarkup menu = null; 724 | switch (lu.QuestionAsked) 725 | { 726 | case QuestionType.ThemeName: 727 | msg = 728 | "Ok, keeping the name. What about the description? Again, press /keep to keep the same:\n" + 729 | lu.ThemeUpdating.Description; 730 | lu.QuestionAsked = QuestionType.Description; 731 | break; 732 | case QuestionType.FileUpload: 733 | msg = "No file change. What about the screenshot? /keep to not change it."; 734 | lu.QuestionAsked = QuestionType.Image; 735 | break; 736 | case QuestionType.Description: 737 | msg = 738 | "Ok, keep the description. How about the file. /keep to keep the same file, or upload a new one to me."; 739 | lu.QuestionAsked = QuestionType.FileUpload; 740 | break; 741 | case QuestionType.Image: 742 | msg = "Got it, keep the image. Do you want your name shown on the listing?"; 743 | menu = 744 | new InlineKeyboardMarkup(new[] 745 | { 746 | new InlineKeyboardButton("Yes", "showname|yes"), 747 | new InlineKeyboardButton("No", "showname|no") 748 | }); 749 | lu.QuestionAsked = QuestionType.ShowOwnerName; 750 | break; 751 | case QuestionType.None: 752 | break; 753 | default: 754 | break; 755 | } 756 | 757 | if (msg != "") 758 | { 759 | Client.SendTextMessageAsync(m.From.Id, msg, replyMarkup: menu); 760 | } 761 | } 762 | break; 763 | case "approval": 764 | try 765 | { 766 | if (m.From.Id == 129046388 || IsModerator(m.From)) 767 | { 768 | using (var db = new tdthemeEntities()) 769 | { 770 | var toApprove = db.Themes.Where(x => x.Approved == null).Take(5).ToList(); 771 | foreach (var t in toApprove) 772 | { 773 | RequestApproval(t.Id); 774 | Thread.Sleep(1000); 775 | } 776 | } 777 | } 778 | } 779 | catch (AggregateException e) 780 | { 781 | Client.SendTextMessageAsync(m.From.Id, e.InnerExceptions[0].Message); 782 | } 783 | catch (Exception e) 784 | { 785 | while (e.InnerException != null) 786 | e = e.InnerException; 787 | Client.SendTextMessageAsync(m.From.Id, e.Message + "\n" + e.StackTrace); 788 | } 789 | break; 790 | case "approve": 791 | try 792 | { 793 | if (m.From.Id == 129046388 || IsModerator(m.From)) 794 | { 795 | var toApprove = int.Parse(m.Text.Split(' ')[1]); 796 | using (var db = new tdthemeEntities()) 797 | { 798 | var t = db.Themes.FirstOrDefault(x => x.Id == toApprove); 799 | if (t == null) 800 | { 801 | Client.SendTextMessageAsync(m.Chat.Id, $"This theme appears to have been removed already.", replyToMessageId: m.MessageId); 802 | return; 803 | } 804 | t.Approved = true; 805 | db.SaveChanges(); 806 | CacheThemes(); 807 | Client.SendTextMessageAsync(t.User.TelegramID, 808 | $"Your theme {t.Name} has been approved and is now listed!"); 809 | } 810 | } 811 | } 812 | catch (AggregateException e) 813 | { 814 | Client.SendTextMessageAsync(m.From.Id, e.InnerExceptions[0].Message); 815 | } 816 | catch (Exception e) 817 | { 818 | while (e.InnerException != null) 819 | e = e.InnerException; 820 | Client.SendTextMessageAsync(m.From.Id, e.Message + "\n" + e.StackTrace); 821 | } 822 | break; 823 | case "disapprove": 824 | try 825 | { 826 | if (m.From.Id == 129046388 || IsModerator(m.From)) 827 | { 828 | var toApprove = int.Parse(m.Text.Split(' ')[1]); 829 | using (var db = new tdthemeEntities()) 830 | { 831 | var t = db.Themes.FirstOrDefault(x => x.Id == toApprove); 832 | t.Approved = false; 833 | db.SaveChanges(); 834 | var id = t.User.TelegramID; 835 | Client.SendTextMessageAsync(id, 836 | $"Your theme {t.Name} was not approved.\n\n{m.Text.Replace("/disapprove " + toApprove, "")}"); 837 | } 838 | } 839 | } 840 | catch (AggregateException e) 841 | { 842 | Client.SendTextMessageAsync(m.From.Id, e.InnerExceptions[0].Message); 843 | } 844 | catch (Exception e) 845 | { 846 | while (e.InnerException != null) 847 | e = e.InnerException; 848 | Client.SendTextMessageAsync(m.From.Id, e.Message + "\n" + e.StackTrace); 849 | } 850 | break; 851 | case "delete": 852 | try 853 | { 854 | if (m.From.Id == 129046388 || IsModerator(m.From)) 855 | { 856 | var toApprove = int.Parse(m.Text.Split(' ')[1]); 857 | using (var db = new tdthemeEntities()) 858 | { 859 | var t = db.Themes.FirstOrDefault(x => x.Id == toApprove); 860 | db.Themes.Remove(t); 861 | db.SaveChanges(); 862 | } 863 | } 864 | } 865 | catch (AggregateException e) 866 | { 867 | Client.SendTextMessageAsync(m.From.Id, e.InnerExceptions[0].Message); 868 | } 869 | catch (Exception e) 870 | { 871 | while (e.InnerException != null) 872 | e = e.InnerException; 873 | Client.SendTextMessageAsync(m.From.Id, e.Message + "\n" + e.StackTrace); 874 | } 875 | break; 876 | case "stats": 877 | try 878 | { 879 | if (m.From.Id == 129046388) 880 | { 881 | using (var db = new tdthemeEntities()) 882 | { 883 | var themes = db.Themes.Count(x => x.Approved == true); 884 | var ratings = db.Ratings.Count(); 885 | var downloads = db.Downloads.Count(); 886 | var users = db.Users.Count(); 887 | Client.SendTextMessageAsync(m.Chat.Id, 888 | $"Themes: {themes}\nDownloads: {downloads}\nRatings: {ratings}\nUsers: {users}"); 889 | } 890 | } 891 | } 892 | catch 893 | { 894 | // ignored 895 | } 896 | break; 897 | case "setmod": 898 | try 899 | { 900 | if (m.From.Id == 129046388) 901 | { 902 | var toApprove = int.Parse(m.Text.Split(' ')[1]); 903 | using (var db = new tdthemeEntities()) 904 | { 905 | var u = db.Users.FirstOrDefault(x => x.TelegramID == toApprove); 906 | if (u.AccessFlags == null) 907 | u.AccessFlags = 0; 908 | var flags = (Access)u.AccessFlags; 909 | if (flags.HasFlag(Access.Moderator)) return; 910 | flags = flags | Access.Moderator; 911 | u.AccessFlags = (int)flags; 912 | db.SaveChanges(); 913 | Client.SendTextMessageAsync(toApprove, 914 | "You now have moderator access.\n/approval - get 5 themes awaiting approval\n/disapprove - disapprove theme, send reason to creator\n/approve - approve theme for publication\n/delete - Delete junk submission"); 915 | Client.SendTextMessageAsync(m.From.Id, 916 | u.Name + " has been set to moderator"); 917 | } 918 | } 919 | } 920 | catch 921 | { 922 | // ignored 923 | } 924 | break; 925 | case "setapproved": 926 | try 927 | { 928 | if (m.From.Id == 129046388) 929 | { 930 | var toApprove = int.Parse(m.Text.Split(' ')[1]); 931 | using (var db = new tdthemeEntities()) 932 | { 933 | var u = db.Users.FirstOrDefault(x => x.TelegramID == toApprove); 934 | if (u.AccessFlags == null) 935 | u.AccessFlags = 0; 936 | var flags = (Access)u.AccessFlags; 937 | if (flags.HasFlag(Access.AutoApprove)) return; 938 | flags = flags | Access.AutoApprove; 939 | u.AccessFlags = (int)flags; 940 | db.SaveChanges(); 941 | Client.SendTextMessageAsync(toApprove, 942 | "You now have auto approval access. New themes you submit will not require a moderator approval, and will be added instantly."); 943 | Client.SendTextMessageAsync(m.From.Id, 944 | u.Name + " has been set to auto approve"); 945 | } 946 | } 947 | } 948 | catch 949 | { 950 | // ignored 951 | } 952 | break; 953 | case "download": 954 | if (m.From.Id != 129046388) return; 955 | DownloadThemes(); 956 | break; 957 | case "cachethemes": 958 | if (m.From.Id != 129046388) return; 959 | CacheThemes(); 960 | break; 961 | case "checkpending": 962 | if (m.From.Id != 129046388) return; 963 | CheckPending(); 964 | break; 965 | } 966 | } 967 | else if (m.Chat.Type == ChatType.Private) 968 | { 969 | //plain text. Are they answering a question, or searching? 970 | var lu = GetLocalUser(m.From.Id); 971 | switch (lu.QuestionAsked) 972 | { 973 | case QuestionType.ThemeName: 974 | if (m.Text.Length >= 3) 975 | { 976 | if (lu.ThemeCreating != null) 977 | lu.ThemeCreating.Name = m.Text; 978 | else 979 | lu.ThemeUpdating.Name = m.Text; 980 | lu.QuestionAsked = QuestionType.Description; 981 | Client.SendTextMessageAsync(lu.Id, 982 | "Alright, now enter a short description of your theme. It helps to use keywords like \"dark\", \"blue\", etc. This will help people locate your theme easier.\nAlso, if your theme is a modification of another theme, please add \"Based on \""); 983 | } 984 | else 985 | { 986 | Client.SendTextMessageAsync(m.From.Id, 987 | "Please enter a name for your theme. It needs to be at least 3 characters."); 988 | } 989 | break; 990 | case QuestionType.FileUpload: 991 | Client.SendTextMessageAsync(m.From.Id, "Please upload a file"); 992 | break; 993 | case QuestionType.Description: 994 | if (m.Text.Length >= 5) 995 | { 996 | if (lu.ThemeCreating != null) 997 | lu.ThemeCreating.Description = m.Text; 998 | else 999 | lu.ThemeUpdating.Description = m.Text; 1000 | lu.QuestionAsked = QuestionType.FileUpload; 1001 | Client.SendTextMessageAsync(lu.Id, "Great, now upload the file to me.\nNotes: If using *nix, please use 7zip, or be careful about what compression method is used. It also helps to create the zip file with .zip, then rename to .tdesktop-theme - not create it AS tdesktop-theme\nMake sure your color file is named colors.tdesktop-theme, and the image is named 'background.jpg', 'background.png', 'tiled.jpg' or 'tiled.png'."); 1002 | } 1003 | else 1004 | { 1005 | Client.SendTextMessageAsync(m.From.Id, 1006 | "Please enter a description. It needs to be at least 5 characters"); 1007 | } 1008 | break; 1009 | case QuestionType.ShowOwnerName: 1010 | break; 1011 | case QuestionType.ShowOwnerUsername: 1012 | break; 1013 | case QuestionType.Image: 1014 | break; 1015 | case QuestionType.None: 1016 | break; 1017 | default: 1018 | throw new ArgumentOutOfRangeException(); 1019 | } 1020 | } 1021 | } 1022 | else if (m.Type == MessageType.DocumentMessage) 1023 | { 1024 | var lu = GetLocalUser(m.From.Id); 1025 | switch (lu.QuestionAsked) 1026 | { 1027 | case QuestionType.FileUpload: 1028 | 1029 | var filename = m.Document.FileName; 1030 | if (!filename.ToLower().EndsWith("tdesktop-theme")) 1031 | { 1032 | Client.SendTextMessageAsync(lu.Id, "Your file needs to be of type `.tdesktop-theme`", 1033 | parseMode: ParseMode.Markdown); 1034 | return; 1035 | } 1036 | var dir = Path.Combine(ThemesDirectory, "..\\Temp\\" + lu.Id); 1037 | Directory.CreateDirectory(dir); 1038 | var path = Path.Combine(dir, filename); 1039 | //download and check the file against the cache 1040 | var fs = new FileStream(path, FileMode.Create); 1041 | var file = Client.GetFileAsync(m.Document.FileId, fs).Result; 1042 | fs.Close(); 1043 | var unzipPath = Path.Combine(dir, "unzip"); 1044 | Directory.CreateDirectory(unzipPath); 1045 | File.Delete(path.Replace(".tdesktop-theme", ".zip")); 1046 | File.Move(path, path.Replace(".tdesktop-theme", ".zip")); 1047 | path = path.Replace(".tdesktop-theme", ".zip"); 1048 | 1049 | using (var zip = ZipFile.Read(path)) 1050 | { 1051 | foreach (var f in zip) 1052 | { 1053 | f.Extract(unzipPath, ExtractExistingFileAction.OverwriteSilently); 1054 | } 1055 | } 1056 | 1057 | var themeFiles = Directory.GetFiles(unzipPath, "*.tdesktop-theme"); 1058 | if (themeFiles.Length > 1) 1059 | { 1060 | Client.SendTextMessageAsync(lu.Id, 1061 | "Your theme file contains more than one tdesktop-theme inside it. Please have only one of these files."); 1062 | break; 1063 | } 1064 | 1065 | if (themeFiles.Length != 1) 1066 | { 1067 | Client.SendTextMessageAsync(lu.Id, "Your theme file contains no theme inside it."); 1068 | break; 1069 | } 1070 | var theme = themeFiles[0]; 1071 | //now read the theme file 1072 | 1073 | var dict = new Dictionary(); 1074 | //read the file 1075 | var line = ""; 1076 | using (var sr = new StreamReader(theme)) 1077 | { 1078 | while ((line = sr.ReadLine()) != null) 1079 | { 1080 | if (String.IsNullOrEmpty(line) || line.StartsWith("//")) continue; 1081 | try 1082 | { 1083 | var lineSplit = line.Split(':'); 1084 | dict.Add(lineSplit[0].Trim(), lineSplit[1].Split(';')[0].Trim()); 1085 | } 1086 | catch 1087 | { 1088 | 1089 | } 1090 | } 1091 | } 1092 | 1093 | //compare to cache 1094 | var same = false; 1095 | foreach (var d in CachedThemes) 1096 | { 1097 | var count = 0; 1098 | foreach (var value in dict) 1099 | { 1100 | if (d.ContainsKey(value.Key)) 1101 | { 1102 | if (d[value.Key] == value.Value) 1103 | { 1104 | count++; 1105 | } 1106 | } 1107 | else 1108 | { 1109 | count++; 1110 | } 1111 | 1112 | } 1113 | 1114 | if (count >= dict.Count) 1115 | { 1116 | same = true; 1117 | } 1118 | 1119 | } 1120 | 1121 | if (same) 1122 | { 1123 | Client.SendTextMessageAsync(lu.Id, 1124 | "This theme matches an existing theme. Simply changing the background, or re-uploading a theme does not count as creating a new theme."); 1125 | break; 1126 | } 1127 | 1128 | 1129 | 1130 | 1131 | if (lu.ThemeCreating != null) 1132 | { 1133 | lu.ThemeCreating.FileName = filename; 1134 | lu.ThemeCreating.File_Id = m.Document.FileId; 1135 | } 1136 | else 1137 | { 1138 | lu.ThemeUpdating.FileName = filename; 1139 | lu.ThemeUpdating.File_Id = m.Document.FileId; 1140 | 1141 | } 1142 | Client.SendTextMessageAsync(lu.Id, 1143 | "Awesome. Now, upload a screenshot that I can show to users when they search for your theme"); 1144 | lu.QuestionAsked = QuestionType.Image; 1145 | //fs.Dispose(); 1146 | //File.Delete(filename); 1147 | 1148 | 1149 | break; 1150 | case QuestionType.Image: 1151 | Client.SendTextMessageAsync(lu.Id, "Please send the photo as compressed, not as a document"); 1152 | break; 1153 | default: 1154 | //didn't ask for a file.... 1155 | break; 1156 | } 1157 | } 1158 | else if (m.Type == MessageType.PhotoMessage) 1159 | { 1160 | var lu = GetLocalUser(m.From.Id); 1161 | switch (lu.QuestionAsked) 1162 | { 1163 | case QuestionType.Image: 1164 | if (lu.ThemeCreating != null) 1165 | lu.ThemeCreating.Photo_Id = m.Photo[0].FileId; 1166 | else 1167 | lu.ThemeUpdating.Photo_Id = m.Photo[0].FileId; 1168 | lu.QuestionAsked = QuestionType.ShowOwnerName; 1169 | Client.SendTextMessageAsync(lu.Id, 1170 | "Got it. Now a couple simple questions.\nFirst, do you want your name to be shown in the search result for your theme?", 1171 | replyMarkup: 1172 | new InlineKeyboardMarkup(new[] 1173 | { 1174 | new InlineKeyboardButton("Yes", "showname|yes"), 1175 | new InlineKeyboardButton("No", "showname|no") 1176 | })); 1177 | 1178 | break; 1179 | default: 1180 | //didn't ask for a photo... 1181 | break; 1182 | } 1183 | } 1184 | } 1185 | catch (AggregateException e) 1186 | { 1187 | Client.SendTextMessageAsync(ThemeGroup, e.InnerExceptions[0].Message); 1188 | Client.ForwardMessageAsync(ThemeGroup, m.Chat.Id, m.MessageId); 1189 | } 1190 | catch (Exception e) 1191 | { 1192 | while (e.InnerException != null) 1193 | e = e.InnerException; 1194 | if (e.Message.Trim().StartsWith("Bad sig")) 1195 | { 1196 | Client.SendTextMessageAsync(m.From.Id, 1197 | $"It looks like you are using *nix. Please try zipping the file with 7zip or something else.\n{e.Message}"); 1198 | } 1199 | else 1200 | { 1201 | Client.SendTextMessageAsync(m.From.Id, e.Message); 1202 | Client.SendTextMessageAsync(ThemeGroup, e.Message + "\n" + e.StackTrace); 1203 | Client.ForwardMessageAsync(ThemeGroup, m.Chat.Id, m.MessageId); 1204 | } 1205 | 1206 | } 1207 | } 1208 | 1209 | private static bool IsModerator(Telegram.Bot.Types.User u) 1210 | { 1211 | using (var db = new tdthemeEntities()) 1212 | { 1213 | var user = db.Users.FirstOrDefault(x => x.TelegramID == u.Id); 1214 | if (user.AccessFlags == null) return false; 1215 | var flags = (Access)user.AccessFlags; 1216 | return flags.HasFlag(Access.Moderator); 1217 | } 1218 | } 1219 | 1220 | private static void CheckPending() 1221 | { 1222 | try 1223 | { 1224 | using (var db = new tdthemeEntities()) 1225 | { 1226 | var pending = db.Themes.Where(x => x.Approved == null); 1227 | foreach (var pend in pending) 1228 | { 1229 | //download the theme 1230 | Console.WriteLine("Checking: " + pend.Name); 1231 | var dir = Path.Combine(ThemesDirectory, "..\\Temp\\" + pend.Id); 1232 | Directory.CreateDirectory(dir); 1233 | var path = Path.Combine(dir, pend.FileName); 1234 | //download and check the file against the cache 1235 | var fs = new FileStream(path, FileMode.Create); 1236 | var file = Client.GetFileAsync(pend.File_Id, fs).Result; 1237 | fs.Close(); 1238 | var unzipPath = Path.Combine(dir, "unzip"); 1239 | Directory.CreateDirectory(unzipPath); 1240 | File.Delete(path.Replace(".tdesktop-theme", ".zip")); 1241 | File.Move(path, path.Replace(".tdesktop-theme", ".zip")); 1242 | path = path.Replace(".tdesktop-theme", ".zip"); 1243 | var id = pend.User.Id; 1244 | try 1245 | { 1246 | using (var zip = ZipFile.Read(path)) 1247 | { 1248 | foreach (var f in zip) 1249 | { 1250 | f.Extract(unzipPath, ExtractExistingFileAction.OverwriteSilently); 1251 | } 1252 | } 1253 | } 1254 | catch (Exception e) 1255 | { 1256 | Console.WriteLine(e.Message); 1257 | Client.SendTextMessageAsync(id, "Your theme file was unable to be unpacked: " + e.Message + "\n please repack and resubmit"); 1258 | pend.Approved = false; 1259 | 1260 | continue; 1261 | } 1262 | 1263 | var themeFiles = Directory.GetFiles(unzipPath, "*.tdesktop-theme"); 1264 | if (themeFiles.Length > 1) 1265 | { 1266 | Console.WriteLine("Multiple color files"); 1267 | Client.SendTextMessageAsync(id, "Your theme file contains more than one tdesktop-theme inside it. Please have only one of these files."); 1268 | pend.Approved = false; 1269 | 1270 | continue; 1271 | } 1272 | 1273 | if (themeFiles.Length != 1) 1274 | { 1275 | Console.WriteLine("No color files"); 1276 | Client.SendTextMessageAsync(id, "Your theme file contains no theme inside it."); 1277 | pend.Approved = false; 1278 | 1279 | continue; 1280 | } 1281 | var theme = themeFiles[0]; 1282 | //now read the theme file 1283 | 1284 | var dict = new Dictionary(); 1285 | //read the file 1286 | var line = ""; 1287 | using (var sr = new StreamReader(theme)) 1288 | { 1289 | while ((line = sr.ReadLine()) != null) 1290 | { 1291 | if (String.IsNullOrEmpty(line) || line.StartsWith("//")) continue; 1292 | try 1293 | { 1294 | var lineSplit = line.Split(':'); 1295 | dict.Add(lineSplit[0].Trim(), lineSplit[1].Split(';')[0].Trim()); 1296 | } 1297 | catch 1298 | { 1299 | 1300 | } 1301 | } 1302 | } 1303 | 1304 | //compare to cache 1305 | var same = false; 1306 | foreach (var d in CachedThemes) 1307 | { 1308 | var count = 0; 1309 | foreach (var value in dict) 1310 | { 1311 | if (d.ContainsKey(value.Key)) 1312 | { 1313 | if (d[value.Key] == value.Value) 1314 | { 1315 | count++; 1316 | } 1317 | } 1318 | else 1319 | { 1320 | count++; 1321 | } 1322 | 1323 | } 1324 | 1325 | if (count >= dict.Count) 1326 | { 1327 | same = true; 1328 | } 1329 | 1330 | } 1331 | 1332 | if (same) 1333 | { 1334 | Console.WriteLine("Matches existing"); 1335 | pend.Approved = false; 1336 | Client.SendTextMessageAsync(id, 1337 | "This theme matches an existing theme. Simply changing the background, or re-uploading a theme does not count as creating a new theme."); 1338 | 1339 | } 1340 | 1341 | } 1342 | 1343 | db.SaveChanges(); 1344 | } 1345 | } 1346 | catch (AggregateException e) 1347 | { 1348 | Client.SendTextMessageAsync(129046388, e.InnerExceptions[0].Message); 1349 | } 1350 | catch (Exception e) 1351 | { 1352 | while (e.InnerException != null) 1353 | e = e.InnerException; 1354 | Client.SendTextMessageAsync(129046388, e.Message + "\n" + e.StackTrace); 1355 | } 1356 | } 1357 | 1358 | private static void DownloadThemes() 1359 | { 1360 | try 1361 | { 1362 | Directory.CreateDirectory(ThemesDirectory); 1363 | var temp = new List(); 1364 | //initialize the folder with all the themes 1365 | using (var db = new tdthemeEntities()) 1366 | { 1367 | foreach (var t in db.Themes.Where(x => x.Approved == true).Include(x => x.User).ToList()) 1368 | { 1369 | temp.Add(t); 1370 | if (Directory.Exists(Path.Combine(ThemesDirectory, t.Id.ToString()))) continue; 1371 | Directory.CreateDirectory(Path.Combine(ThemesDirectory, t.Id.ToString())); 1372 | var path = Path.Combine(ThemesDirectory, t.Id + "\\" + t.FileName); 1373 | using (var fs = new FileStream(path, FileMode.Create)) 1374 | { 1375 | var res = Client.GetFileAsync(t.File_Id, fs).Result; 1376 | //wait for the file to download 1377 | fs.Close(); 1378 | } 1379 | Thread.Sleep(500); 1380 | } 1381 | } 1382 | LoadedThemes.Clear(); 1383 | LoadedThemes.AddRange(temp); 1384 | } 1385 | catch (AggregateException e) 1386 | { 1387 | Client.SendTextMessageAsync(129046388, e.InnerExceptions[0].Message); 1388 | } 1389 | catch (Exception e) 1390 | { 1391 | while (e.InnerException != null) 1392 | e = e.InnerException; 1393 | Client.SendTextMessageAsync(129046388, e.Message + "\n" + e.StackTrace); 1394 | } 1395 | } 1396 | 1397 | private static void CacheThemes() 1398 | { 1399 | #if !DEBUG 1400 | DownloadThemes(); 1401 | #endif 1402 | try 1403 | { 1404 | var temp = new List>(); 1405 | foreach (var d in Directory.GetDirectories(ThemesDirectory)) 1406 | { 1407 | Console.WriteLine("Caching: " + d.Split('\\').Last()); 1408 | var path = Directory.GetFiles(d, "*.tdesktop-theme").FirstOrDefault(); 1409 | if (path == null) 1410 | path = Directory.GetFiles(d, "*.zip").FirstOrDefault(); 1411 | if (path != null) 1412 | { 1413 | if (!path.EndsWith("zip")) 1414 | if (File.Exists(path)) 1415 | File.Move(path, path.Replace(".tdesktop-theme", ".zip")); 1416 | path = path.Replace(".tdesktop-theme", ".zip"); 1417 | var unzipPath = Path.Combine(d, "unzip"); 1418 | Directory.CreateDirectory(unzipPath); 1419 | try 1420 | { 1421 | //unzip 1422 | using (var zip = ZipFile.Read(path)) 1423 | { 1424 | foreach (ZipEntry f in zip) 1425 | { 1426 | f.Extract(unzipPath, ExtractExistingFileAction.OverwriteSilently); 1427 | } 1428 | } 1429 | } 1430 | catch 1431 | { 1432 | //bad zip? 1433 | File.Delete(path); 1434 | Directory.Delete(unzipPath, true); 1435 | Directory.Delete(d, true); 1436 | continue; 1437 | } 1438 | 1439 | var themeFile = 1440 | Directory.GetFiles(unzipPath, "*.tdesktop-theme").FirstOrDefault(); 1441 | if (themeFile != null) 1442 | { 1443 | var dict = new Dictionary(); 1444 | //read the file 1445 | var line = ""; 1446 | using (var sr = new StreamReader(themeFile)) 1447 | { 1448 | while ((line = sr.ReadLine()) != null) 1449 | { 1450 | if (String.IsNullOrEmpty(line) || line.StartsWith("//")) continue; 1451 | try 1452 | { 1453 | var lineSplit = line.Split(':'); 1454 | dict.Add(lineSplit[0].Trim(), lineSplit[1].Split(';')[0].Trim()); 1455 | } 1456 | catch 1457 | { 1458 | 1459 | } 1460 | } 1461 | } 1462 | if (dict.Any()) 1463 | temp.Add(dict); 1464 | } 1465 | } 1466 | } 1467 | CachedThemes.Clear(); 1468 | CachedThemes.AddRange(temp); 1469 | } 1470 | catch (AggregateException e) 1471 | { 1472 | Client.SendTextMessageAsync(129046388, e.InnerExceptions[0].Message); 1473 | } 1474 | catch (Exception e) 1475 | { 1476 | while (e.InnerException != null) 1477 | e = e.InnerException; 1478 | Client.SendTextMessageAsync(129046388, e.Message + "\n" + e.StackTrace); 1479 | } 1480 | 1481 | Console.WriteLine("Themes in cache: " + CachedThemes.Count); 1482 | } 1483 | 1484 | private static LUser GetLocalUser(int id) 1485 | { 1486 | if (LocalUsers.All(x => x.Id != id)) 1487 | LocalUsers.Add(new LUser { Id = id }); 1488 | return LocalUsers.FirstOrDefault(x => x.Id == id); 1489 | } 1490 | 1491 | private static void CreateOrUpdateDBUser(Telegram.Bot.Types.User u) 1492 | { 1493 | new Task(() => 1494 | { 1495 | using (var db = new tdthemeEntities()) 1496 | { 1497 | try 1498 | { 1499 | var usr = db.Users.FirstOrDefault(x => x.TelegramID == u.Id); 1500 | if (usr == null) 1501 | { 1502 | usr = new User 1503 | { 1504 | Name = (u.FirstName + " " + u.LastName).Trim(), 1505 | TelegramID = u.Id, 1506 | Username = u.Username 1507 | }; 1508 | db.Users.Add(usr); 1509 | db.SaveChanges(); 1510 | } 1511 | else //update name information 1512 | { 1513 | usr.Name = (u.FirstName + " " + u.LastName).Trim(); 1514 | usr.Username = u.Username; 1515 | db.SaveChanges(); 1516 | } 1517 | } 1518 | catch (Exception e) 1519 | { 1520 | //TODO: Add logging 1521 | } 1522 | } 1523 | }).Start(); 1524 | } 1525 | } 1526 | } 1527 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ThemeBot")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ThemeBot")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("421a17a6-c57d-45a4-b37d-985232e3a86d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/QuestionType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ThemeBot 8 | { 9 | public enum QuestionType 10 | { 11 | ThemeName, FileUpload, Description, ShowOwnerName, ShowOwnerUsername, Image, None 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/Rating.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace ThemeBot 11 | { 12 | using System; 13 | using System.Collections.Generic; 14 | 15 | public partial class Rating 16 | { 17 | public int ThemeId { get; set; } 18 | public int UserId { get; set; } 19 | public int Rating1 { get; set; } 20 | public Nullable TimeStamp { get; set; } 21 | public int Id { get; set; } 22 | 23 | public virtual Theme Theme { get; set; } 24 | public virtual User User { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/Theme.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace ThemeBot 11 | { 12 | using System; 13 | using System.Collections.Generic; 14 | 15 | public partial class Theme 16 | { 17 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] 18 | public Theme() 19 | { 20 | this.Downloads = new HashSet(); 21 | this.Ratings = new HashSet(); 22 | } 23 | 24 | public int Id { get; set; } 25 | public string Name { get; set; } 26 | public string Description { get; set; } 27 | public bool ShowOwnerName { get; set; } 28 | public bool ShowOwnerUsername { get; set; } 29 | public int OwnerID { get; set; } 30 | public string FileName { get; set; } 31 | public string File_Id { get; set; } 32 | public string Photo_Id { get; set; } 33 | public Nullable TimesChosen { get; set; } 34 | public Nullable LastUpdated { get; set; } 35 | public Nullable Approved { get; set; } 36 | 37 | public virtual User User { get; set; } 38 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 39 | public virtual ICollection Downloads { get; set; } 40 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 41 | public virtual ICollection Ratings { get; set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/ThemeBot.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {421A17A6-C57D-45A4-B37D-985232E3A86D} 8 | Exe 9 | Properties 10 | ThemeBot 11 | ThemeBot 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\packages\DotNetZip.1.10.1\lib\net20\DotNetZip.dll 38 | True 39 | 40 | 41 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll 42 | True 43 | 44 | 45 | ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll 46 | True 47 | 48 | 49 | ..\packages\Newtonsoft.Json.9.0.2-beta1\lib\net45\Newtonsoft.Json.dll 50 | True 51 | 52 | 53 | 54 | 55 | 56 | 57 | ..\packages\System.Net.Http.2.0.20126.16343\lib\net40\System.Net.Http.dll 58 | True 59 | 60 | 61 | ..\packages\System.Net.Http.Formatting.Extension.5.2.3.0\lib\System.Net.Http.Extensions.dll 62 | True 63 | 64 | 65 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 66 | True 67 | 68 | 69 | ..\packages\System.Net.Http.Formatting.Extension.5.2.3.0\lib\System.Net.Http.Primitives.dll 70 | True 71 | 72 | 73 | ..\packages\System.Net.Http.2.0.20126.16343\lib\net40\System.Net.Http.WebRequest.dll 74 | True 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | ..\packages\Telegram.Bot.11.1.0-beta-3\lib\net45\Telegram.Bot.dll 85 | True 86 | 87 | 88 | 89 | 90 | 91 | ThemeModel.tt 92 | 93 | 94 | 95 | 96 | 97 | 98 | ThemeModel.tt 99 | 100 | 101 | ThemeModel.tt 102 | 103 | 104 | True 105 | True 106 | ThemeModel.Context.tt 107 | 108 | 109 | True 110 | True 111 | ThemeModel.tt 112 | 113 | 114 | True 115 | True 116 | ThemeModel.edmx 117 | 118 | 119 | ThemeModel.tt 120 | 121 | 122 | 123 | 124 | 125 | 126 | EntityModelCodeGenerator 127 | ThemeModel.Designer.cs 128 | 129 | 130 | ThemeModel.edmx 131 | 132 | 133 | 134 | 135 | TextTemplatingFileGenerator 136 | ThemeModel.edmx 137 | ThemeModel.Context.cs 138 | 139 | 140 | TextTemplatingFileGenerator 141 | ThemeModel.edmx 142 | ThemeModel.cs 143 | 144 | 145 | 146 | 147 | 148 | 149 | 156 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/ThemeModel.Context.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace ThemeBot 11 | { 12 | using System; 13 | using System.Data.Entity; 14 | using Microsoft.Win32; 15 | using System.Data.Entity.Infrastructure; 16 | 17 | public partial class tdthemeEntities : DbContext 18 | { 19 | public tdthemeEntities() 20 | : base(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey("SOFTWARE\\ThemeBot").GetValue("BotConnectionString").ToString()) 21 | { 22 | } 23 | 24 | protected override void OnModelCreating(DbModelBuilder modelBuilder) 25 | { 26 | throw new UnintentionalCodeFirstException(); 27 | } 28 | 29 | public virtual DbSet Themes { get; set; } 30 | public virtual DbSet Users { get; set; } 31 | public virtual DbSet Downloads { get; set; } 32 | public virtual DbSet Ratings { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/ThemeModel.Context.tt: -------------------------------------------------------------------------------- 1 | <#@ template language="C#" debug="false" hostspecific="true"#> 2 | <#@ include file="EF6.Utility.CS.ttinclude"#><#@ 3 | output extension=".cs"#><# 4 | 5 | const string inputFile = @"ThemeModel.edmx"; 6 | var textTransform = DynamicTextTransformation.Create(this); 7 | var code = new CodeGenerationTools(this); 8 | var ef = new MetadataTools(this); 9 | var typeMapper = new TypeMapper(code, ef, textTransform.Errors); 10 | var loader = new EdmMetadataLoader(textTransform.Host, textTransform.Errors); 11 | var itemCollection = loader.CreateEdmItemCollection(inputFile); 12 | var modelNamespace = loader.GetModelNamespace(inputFile); 13 | var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); 14 | 15 | var container = itemCollection.OfType().FirstOrDefault(); 16 | if (container == null) 17 | { 18 | return string.Empty; 19 | } 20 | #> 21 | //------------------------------------------------------------------------------ 22 | // 23 | // <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#> 24 | // 25 | // <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#> 26 | // <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine3")#> 27 | // 28 | //------------------------------------------------------------------------------ 29 | 30 | <# 31 | 32 | var codeNamespace = code.VsNamespaceSuggestion(); 33 | if (!String.IsNullOrEmpty(codeNamespace)) 34 | { 35 | #> 36 | namespace <#=code.EscapeNamespace(codeNamespace)#> 37 | { 38 | <# 39 | PushIndent(" "); 40 | } 41 | 42 | #> 43 | using System; 44 | using System.Data.Entity; 45 | using Microsoft.Win32; 46 | using System.Data.Entity.Infrastructure; 47 | <# 48 | if (container.FunctionImports.Any()) 49 | { 50 | #> 51 | using System.Data.Entity.Core.Objects; 52 | using System.Linq; 53 | <# 54 | } 55 | #> 56 | 57 | <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext 58 | { 59 | public <#=code.Escape(container)#>() 60 | : base(RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey("SOFTWARE\\ThemeBot").GetValue("BotConnectionString").ToString()) 61 | { 62 | <# 63 | if (!loader.IsLazyLoadingEnabled(container)) 64 | { 65 | #> 66 | this.Configuration.LazyLoadingEnabled = false; 67 | <# 68 | } 69 | 70 | foreach (var entitySet in container.BaseEntitySets.OfType()) 71 | { 72 | // Note: the DbSet members are defined below such that the getter and 73 | // setter always have the same accessibility as the DbSet definition 74 | if (Accessibility.ForReadOnlyProperty(entitySet) != "public") 75 | { 76 | #> 77 | <#=codeStringGenerator.DbSetInitializer(entitySet)#> 78 | <# 79 | } 80 | } 81 | #> 82 | } 83 | 84 | protected override void OnModelCreating(DbModelBuilder modelBuilder) 85 | { 86 | throw new UnintentionalCodeFirstException(); 87 | } 88 | 89 | <# 90 | foreach (var entitySet in container.BaseEntitySets.OfType()) 91 | { 92 | #> 93 | <#=codeStringGenerator.DbSet(entitySet)#> 94 | <# 95 | } 96 | 97 | foreach (var edmFunction in container.FunctionImports) 98 | { 99 | WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: false); 100 | } 101 | #> 102 | } 103 | <# 104 | 105 | if (!String.IsNullOrEmpty(codeNamespace)) 106 | { 107 | PopIndent(); 108 | #> 109 | } 110 | <# 111 | } 112 | #> 113 | <#+ 114 | 115 | private void WriteFunctionImport(TypeMapper typeMapper, CodeStringGenerator codeStringGenerator, EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) 116 | { 117 | if (typeMapper.IsComposable(edmFunction)) 118 | { 119 | #> 120 | 121 | [DbFunction("<#=edmFunction.NamespaceName#>", "<#=edmFunction.Name#>")] 122 | <#=codeStringGenerator.ComposableFunctionMethod(edmFunction, modelNamespace)#> 123 | { 124 | <#+ 125 | codeStringGenerator.WriteFunctionParameters(edmFunction, WriteFunctionParameter); 126 | #> 127 | <#=codeStringGenerator.ComposableCreateQuery(edmFunction, modelNamespace)#> 128 | } 129 | <#+ 130 | } 131 | else 132 | { 133 | #> 134 | 135 | <#=codeStringGenerator.FunctionMethod(edmFunction, modelNamespace, includeMergeOption)#> 136 | { 137 | <#+ 138 | codeStringGenerator.WriteFunctionParameters(edmFunction, WriteFunctionParameter); 139 | #> 140 | <#=codeStringGenerator.ExecuteFunction(edmFunction, modelNamespace, includeMergeOption)#> 141 | } 142 | <#+ 143 | if (typeMapper.GenerateMergeOptionFunction(edmFunction, includeMergeOption)) 144 | { 145 | WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: true); 146 | } 147 | } 148 | } 149 | 150 | public void WriteFunctionParameter(string name, string isNotNull, string notNullInit, string nullInit) 151 | { 152 | #> 153 | var <#=name#> = <#=isNotNull#> ? 154 | <#=notNullInit#> : 155 | <#=nullInit#>; 156 | 157 | <#+ 158 | } 159 | 160 | public const string TemplateId = "CSharp_DbContext_Context_EF6"; 161 | 162 | public class CodeStringGenerator 163 | { 164 | private readonly CodeGenerationTools _code; 165 | private readonly TypeMapper _typeMapper; 166 | private readonly MetadataTools _ef; 167 | 168 | public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef) 169 | { 170 | ArgumentNotNull(code, "code"); 171 | ArgumentNotNull(typeMapper, "typeMapper"); 172 | ArgumentNotNull(ef, "ef"); 173 | 174 | _code = code; 175 | _typeMapper = typeMapper; 176 | _ef = ef; 177 | } 178 | 179 | public string Property(EdmProperty edmProperty) 180 | { 181 | return string.Format( 182 | CultureInfo.InvariantCulture, 183 | "{0} {1} {2} {{ {3}get; {4}set; }}", 184 | Accessibility.ForProperty(edmProperty), 185 | _typeMapper.GetTypeName(edmProperty.TypeUsage), 186 | _code.Escape(edmProperty), 187 | _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), 188 | _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); 189 | } 190 | 191 | public string NavigationProperty(NavigationProperty navProp) 192 | { 193 | var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType()); 194 | return string.Format( 195 | CultureInfo.InvariantCulture, 196 | "{0} {1} {2} {{ {3}get; {4}set; }}", 197 | AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)), 198 | navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, 199 | _code.Escape(navProp), 200 | _code.SpaceAfter(Accessibility.ForGetter(navProp)), 201 | _code.SpaceAfter(Accessibility.ForSetter(navProp))); 202 | } 203 | 204 | public string AccessibilityAndVirtual(string accessibility) 205 | { 206 | return accessibility + (accessibility != "private" ? " virtual" : ""); 207 | } 208 | 209 | public string EntityClassOpening(EntityType entity) 210 | { 211 | return string.Format( 212 | CultureInfo.InvariantCulture, 213 | "{0} {1}partial class {2}{3}", 214 | Accessibility.ForType(entity), 215 | _code.SpaceAfter(_code.AbstractOption(entity)), 216 | _code.Escape(entity), 217 | _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); 218 | } 219 | 220 | public string EnumOpening(SimpleType enumType) 221 | { 222 | return string.Format( 223 | CultureInfo.InvariantCulture, 224 | "{0} enum {1} : {2}", 225 | Accessibility.ForType(enumType), 226 | _code.Escape(enumType), 227 | _code.Escape(_typeMapper.UnderlyingClrType(enumType))); 228 | } 229 | 230 | public void WriteFunctionParameters(EdmFunction edmFunction, Action writeParameter) 231 | { 232 | var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); 233 | foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable)) 234 | { 235 | var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null"; 236 | var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + parameter.FunctionParameterName + ")"; 237 | var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + TypeMapper.FixNamespaces(parameter.RawClrTypeName) + "))"; 238 | writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit); 239 | } 240 | } 241 | 242 | public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace) 243 | { 244 | var parameters = _typeMapper.GetParameters(edmFunction); 245 | 246 | return string.Format( 247 | CultureInfo.InvariantCulture, 248 | "{0} IQueryable<{1}> {2}({3})", 249 | AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), 250 | _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), 251 | _code.Escape(edmFunction), 252 | string.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray())); 253 | } 254 | 255 | public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace) 256 | { 257 | var parameters = _typeMapper.GetParameters(edmFunction); 258 | 259 | return string.Format( 260 | CultureInfo.InvariantCulture, 261 | "return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});", 262 | _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), 263 | edmFunction.NamespaceName, 264 | edmFunction.Name, 265 | string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()), 266 | _code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()))); 267 | } 268 | 269 | public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) 270 | { 271 | var parameters = _typeMapper.GetParameters(edmFunction); 272 | var returnType = _typeMapper.GetReturnType(edmFunction); 273 | 274 | var paramList = String.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray()); 275 | if (includeMergeOption) 276 | { 277 | paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption"; 278 | } 279 | 280 | return string.Format( 281 | CultureInfo.InvariantCulture, 282 | "{0} {1} {2}({3})", 283 | AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), 284 | returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", 285 | _code.Escape(edmFunction), 286 | paramList); 287 | } 288 | 289 | public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) 290 | { 291 | var parameters = _typeMapper.GetParameters(edmFunction); 292 | var returnType = _typeMapper.GetReturnType(edmFunction); 293 | 294 | var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())); 295 | if (includeMergeOption) 296 | { 297 | callParams = ", mergeOption" + callParams; 298 | } 299 | 300 | return string.Format( 301 | CultureInfo.InvariantCulture, 302 | "return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});", 303 | returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", 304 | edmFunction.Name, 305 | callParams); 306 | } 307 | 308 | public string DbSet(EntitySet entitySet) 309 | { 310 | return string.Format( 311 | CultureInfo.InvariantCulture, 312 | "{0} virtual DbSet<{1}> {2} {{ get; set; }}", 313 | Accessibility.ForReadOnlyProperty(entitySet), 314 | _typeMapper.GetTypeName(entitySet.ElementType), 315 | _code.Escape(entitySet)); 316 | } 317 | 318 | public string DbSetInitializer(EntitySet entitySet) 319 | { 320 | return string.Format( 321 | CultureInfo.InvariantCulture, 322 | "{0} = Set<{1}>();", 323 | _code.Escape(entitySet), 324 | _typeMapper.GetTypeName(entitySet.ElementType)); 325 | } 326 | 327 | public string UsingDirectives(bool inHeader, bool includeCollections = true) 328 | { 329 | return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) 330 | ? string.Format( 331 | CultureInfo.InvariantCulture, 332 | "{0}using System;{1}" + 333 | "{2}", 334 | inHeader ? Environment.NewLine : "", 335 | includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "", 336 | inHeader ? "" : Environment.NewLine) 337 | : ""; 338 | } 339 | } 340 | 341 | public class TypeMapper 342 | { 343 | private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; 344 | 345 | private readonly System.Collections.IList _errors; 346 | private readonly CodeGenerationTools _code; 347 | private readonly MetadataTools _ef; 348 | 349 | public static string FixNamespaces(string typeName) 350 | { 351 | return typeName.Replace("System.Data.Spatial.", "System.Data.Entity.Spatial."); 352 | } 353 | 354 | public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors) 355 | { 356 | ArgumentNotNull(code, "code"); 357 | ArgumentNotNull(ef, "ef"); 358 | ArgumentNotNull(errors, "errors"); 359 | 360 | _code = code; 361 | _ef = ef; 362 | _errors = errors; 363 | } 364 | 365 | public string GetTypeName(TypeUsage typeUsage) 366 | { 367 | return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null); 368 | } 369 | 370 | public string GetTypeName(EdmType edmType) 371 | { 372 | return GetTypeName(edmType, isNullable: null, modelNamespace: null); 373 | } 374 | 375 | public string GetTypeName(TypeUsage typeUsage, string modelNamespace) 376 | { 377 | return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace); 378 | } 379 | 380 | public string GetTypeName(EdmType edmType, string modelNamespace) 381 | { 382 | return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace); 383 | } 384 | 385 | public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace) 386 | { 387 | if (edmType == null) 388 | { 389 | return null; 390 | } 391 | 392 | var collectionType = edmType as CollectionType; 393 | if (collectionType != null) 394 | { 395 | return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace)); 396 | } 397 | 398 | var typeName = _code.Escape(edmType.MetadataProperties 399 | .Where(p => p.Name == ExternalTypeNameAttributeName) 400 | .Select(p => (string)p.Value) 401 | .FirstOrDefault()) 402 | ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ? 403 | _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) : 404 | _code.Escape(edmType)); 405 | 406 | if (edmType is StructuralType) 407 | { 408 | return typeName; 409 | } 410 | 411 | if (edmType is SimpleType) 412 | { 413 | var clrType = UnderlyingClrType(edmType); 414 | if (!IsEnumType(edmType)) 415 | { 416 | typeName = _code.Escape(clrType); 417 | } 418 | 419 | typeName = FixNamespaces(typeName); 420 | 421 | return clrType.IsValueType && isNullable == true ? 422 | String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) : 423 | typeName; 424 | } 425 | 426 | throw new ArgumentException("edmType"); 427 | } 428 | 429 | public Type UnderlyingClrType(EdmType edmType) 430 | { 431 | ArgumentNotNull(edmType, "edmType"); 432 | 433 | var primitiveType = edmType as PrimitiveType; 434 | if (primitiveType != null) 435 | { 436 | return primitiveType.ClrEquivalentType; 437 | } 438 | 439 | if (IsEnumType(edmType)) 440 | { 441 | return GetEnumUnderlyingType(edmType).ClrEquivalentType; 442 | } 443 | 444 | return typeof(object); 445 | } 446 | 447 | public object GetEnumMemberValue(MetadataItem enumMember) 448 | { 449 | ArgumentNotNull(enumMember, "enumMember"); 450 | 451 | var valueProperty = enumMember.GetType().GetProperty("Value"); 452 | return valueProperty == null ? null : valueProperty.GetValue(enumMember, null); 453 | } 454 | 455 | public string GetEnumMemberName(MetadataItem enumMember) 456 | { 457 | ArgumentNotNull(enumMember, "enumMember"); 458 | 459 | var nameProperty = enumMember.GetType().GetProperty("Name"); 460 | return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null); 461 | } 462 | 463 | public System.Collections.IEnumerable GetEnumMembers(EdmType enumType) 464 | { 465 | ArgumentNotNull(enumType, "enumType"); 466 | 467 | var membersProperty = enumType.GetType().GetProperty("Members"); 468 | return membersProperty != null 469 | ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null) 470 | : Enumerable.Empty(); 471 | } 472 | 473 | public bool EnumIsFlags(EdmType enumType) 474 | { 475 | ArgumentNotNull(enumType, "enumType"); 476 | 477 | var isFlagsProperty = enumType.GetType().GetProperty("IsFlags"); 478 | return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null); 479 | } 480 | 481 | public bool IsEnumType(GlobalItem edmType) 482 | { 483 | ArgumentNotNull(edmType, "edmType"); 484 | 485 | return edmType.GetType().Name == "EnumType"; 486 | } 487 | 488 | public PrimitiveType GetEnumUnderlyingType(EdmType enumType) 489 | { 490 | ArgumentNotNull(enumType, "enumType"); 491 | 492 | return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null); 493 | } 494 | 495 | public string CreateLiteral(object value) 496 | { 497 | if (value == null || value.GetType() != typeof(TimeSpan)) 498 | { 499 | return _code.CreateLiteral(value); 500 | } 501 | 502 | return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks); 503 | } 504 | 505 | public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable types, string sourceFile) 506 | { 507 | ArgumentNotNull(types, "types"); 508 | ArgumentNotNull(sourceFile, "sourceFile"); 509 | 510 | var hash = new HashSet(StringComparer.InvariantCultureIgnoreCase); 511 | if (types.Any(item => !hash.Add(item))) 512 | { 513 | _errors.Add( 514 | new CompilerError(sourceFile, -1, -1, "6023", 515 | String.Format(CultureInfo.CurrentCulture, CodeGenerationTools.GetResourceString("Template_CaseInsensitiveTypeConflict")))); 516 | return false; 517 | } 518 | return true; 519 | } 520 | 521 | public IEnumerable GetEnumItemsToGenerate(IEnumerable itemCollection) 522 | { 523 | return GetItemsToGenerate(itemCollection) 524 | .Where(e => IsEnumType(e)); 525 | } 526 | 527 | public IEnumerable GetItemsToGenerate(IEnumerable itemCollection) where T: EdmType 528 | { 529 | return itemCollection 530 | .OfType() 531 | .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) 532 | .OrderBy(i => i.Name); 533 | } 534 | 535 | public IEnumerable GetAllGlobalItems(IEnumerable itemCollection) 536 | { 537 | return itemCollection 538 | .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i)) 539 | .Select(g => GetGlobalItemName(g)); 540 | } 541 | 542 | public string GetGlobalItemName(GlobalItem item) 543 | { 544 | if (item is EdmType) 545 | { 546 | return ((EdmType)item).Name; 547 | } 548 | else 549 | { 550 | return ((EntityContainer)item).Name; 551 | } 552 | } 553 | 554 | public IEnumerable GetSimpleProperties(EntityType type) 555 | { 556 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); 557 | } 558 | 559 | public IEnumerable GetSimpleProperties(ComplexType type) 560 | { 561 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); 562 | } 563 | 564 | public IEnumerable GetComplexProperties(EntityType type) 565 | { 566 | return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); 567 | } 568 | 569 | public IEnumerable GetComplexProperties(ComplexType type) 570 | { 571 | return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); 572 | } 573 | 574 | public IEnumerable GetPropertiesWithDefaultValues(EntityType type) 575 | { 576 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); 577 | } 578 | 579 | public IEnumerable GetPropertiesWithDefaultValues(ComplexType type) 580 | { 581 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); 582 | } 583 | 584 | public IEnumerable GetNavigationProperties(EntityType type) 585 | { 586 | return type.NavigationProperties.Where(np => np.DeclaringType == type); 587 | } 588 | 589 | public IEnumerable GetCollectionNavigationProperties(EntityType type) 590 | { 591 | return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many); 592 | } 593 | 594 | public FunctionParameter GetReturnParameter(EdmFunction edmFunction) 595 | { 596 | ArgumentNotNull(edmFunction, "edmFunction"); 597 | 598 | var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters"); 599 | return returnParamsProperty == null 600 | ? edmFunction.ReturnParameter 601 | : ((IEnumerable)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault(); 602 | } 603 | 604 | public bool IsComposable(EdmFunction edmFunction) 605 | { 606 | ArgumentNotNull(edmFunction, "edmFunction"); 607 | 608 | var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute"); 609 | return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null); 610 | } 611 | 612 | public IEnumerable GetParameters(EdmFunction edmFunction) 613 | { 614 | return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); 615 | } 616 | 617 | public TypeUsage GetReturnType(EdmFunction edmFunction) 618 | { 619 | var returnParam = GetReturnParameter(edmFunction); 620 | return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage); 621 | } 622 | 623 | public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption) 624 | { 625 | var returnType = GetReturnType(edmFunction); 626 | return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType; 627 | } 628 | } 629 | 630 | public static void ArgumentNotNull(T arg, string name) where T : class 631 | { 632 | if (arg == null) 633 | { 634 | throw new ArgumentNullException(name); 635 | } 636 | } 637 | #> -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/ThemeModel.Designer.cs: -------------------------------------------------------------------------------- 1 | // T4 code generation is enabled for model 'C:\Users\jus00091\Source\Repos\Telegram-Theme-Bot\ThemeBot\ThemeBot\ThemeModel.edmx'. 2 | // To enable legacy code generation, change the value of the 'Code Generation Strategy' designer 3 | // property to 'Legacy ObjectContext'. This property is available in the Properties Window when the model 4 | // is open in the designer. 5 | 6 | // If no context and entity classes have been generated, it may be because you created an empty model but 7 | // have not yet chosen which version of Entity Framework to use. To generate a context class and entity 8 | // classes for your model, open the model in the designer, right-click on the designer surface, and 9 | // select 'Update Model from Database...', 'Generate Database from Model...', or 'Add Code Generation 10 | // Item...'. -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/ThemeModel.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/ThemeModel.edmx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 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 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/ThemeModel.edmx.diagram: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/ThemeModel.tt: -------------------------------------------------------------------------------- 1 | <#@ template language="C#" debug="false" hostspecific="true"#> 2 | <#@ include file="EF6.Utility.CS.ttinclude"#><#@ 3 | output extension=".cs"#><# 4 | 5 | const string inputFile = @"ThemeModel.edmx"; 6 | var textTransform = DynamicTextTransformation.Create(this); 7 | var code = new CodeGenerationTools(this); 8 | var ef = new MetadataTools(this); 9 | var typeMapper = new TypeMapper(code, ef, textTransform.Errors); 10 | var fileManager = EntityFrameworkTemplateFileManager.Create(this); 11 | var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile); 12 | var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); 13 | 14 | if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile)) 15 | { 16 | return string.Empty; 17 | } 18 | 19 | WriteHeader(codeStringGenerator, fileManager); 20 | 21 | foreach (var entity in typeMapper.GetItemsToGenerate(itemCollection)) 22 | { 23 | fileManager.StartNewFile(entity.Name + ".cs"); 24 | BeginNamespace(code); 25 | #> 26 | <#=codeStringGenerator.UsingDirectives(inHeader: false)#> 27 | <#=codeStringGenerator.EntityClassOpening(entity)#> 28 | { 29 | <# 30 | var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity); 31 | var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity); 32 | var complexProperties = typeMapper.GetComplexProperties(entity); 33 | 34 | if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any()) 35 | { 36 | #> 37 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] 38 | public <#=code.Escape(entity)#>() 39 | { 40 | <# 41 | foreach (var edmProperty in propertiesWithDefaultValues) 42 | { 43 | #> 44 | this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; 45 | <# 46 | } 47 | 48 | foreach (var navigationProperty in collectionNavigationProperties) 49 | { 50 | #> 51 | this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>(); 52 | <# 53 | } 54 | 55 | foreach (var complexProperty in complexProperties) 56 | { 57 | #> 58 | this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); 59 | <# 60 | } 61 | #> 62 | } 63 | 64 | <# 65 | } 66 | 67 | var simpleProperties = typeMapper.GetSimpleProperties(entity); 68 | if (simpleProperties.Any()) 69 | { 70 | foreach (var edmProperty in simpleProperties) 71 | { 72 | #> 73 | <#=codeStringGenerator.Property(edmProperty)#> 74 | <# 75 | } 76 | } 77 | 78 | if (complexProperties.Any()) 79 | { 80 | #> 81 | 82 | <# 83 | foreach(var complexProperty in complexProperties) 84 | { 85 | #> 86 | <#=codeStringGenerator.Property(complexProperty)#> 87 | <# 88 | } 89 | } 90 | 91 | var navigationProperties = typeMapper.GetNavigationProperties(entity); 92 | if (navigationProperties.Any()) 93 | { 94 | #> 95 | 96 | <# 97 | foreach (var navigationProperty in navigationProperties) 98 | { 99 | if (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many) 100 | { 101 | #> 102 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 103 | <# 104 | } 105 | #> 106 | <#=codeStringGenerator.NavigationProperty(navigationProperty)#> 107 | <# 108 | } 109 | } 110 | #> 111 | } 112 | <# 113 | EndNamespace(code); 114 | } 115 | 116 | foreach (var complex in typeMapper.GetItemsToGenerate(itemCollection)) 117 | { 118 | fileManager.StartNewFile(complex.Name + ".cs"); 119 | BeginNamespace(code); 120 | #> 121 | <#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#> 122 | <#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> 123 | { 124 | <# 125 | var complexProperties = typeMapper.GetComplexProperties(complex); 126 | var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex); 127 | 128 | if (propertiesWithDefaultValues.Any() || complexProperties.Any()) 129 | { 130 | #> 131 | public <#=code.Escape(complex)#>() 132 | { 133 | <# 134 | foreach (var edmProperty in propertiesWithDefaultValues) 135 | { 136 | #> 137 | this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; 138 | <# 139 | } 140 | 141 | foreach (var complexProperty in complexProperties) 142 | { 143 | #> 144 | this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); 145 | <# 146 | } 147 | #> 148 | } 149 | 150 | <# 151 | } 152 | 153 | var simpleProperties = typeMapper.GetSimpleProperties(complex); 154 | if (simpleProperties.Any()) 155 | { 156 | foreach(var edmProperty in simpleProperties) 157 | { 158 | #> 159 | <#=codeStringGenerator.Property(edmProperty)#> 160 | <# 161 | } 162 | } 163 | 164 | if (complexProperties.Any()) 165 | { 166 | #> 167 | 168 | <# 169 | foreach(var edmProperty in complexProperties) 170 | { 171 | #> 172 | <#=codeStringGenerator.Property(edmProperty)#> 173 | <# 174 | } 175 | } 176 | #> 177 | } 178 | <# 179 | EndNamespace(code); 180 | } 181 | 182 | foreach (var enumType in typeMapper.GetEnumItemsToGenerate(itemCollection)) 183 | { 184 | fileManager.StartNewFile(enumType.Name + ".cs"); 185 | BeginNamespace(code); 186 | #> 187 | <#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#> 188 | <# 189 | if (typeMapper.EnumIsFlags(enumType)) 190 | { 191 | #> 192 | [Flags] 193 | <# 194 | } 195 | #> 196 | <#=codeStringGenerator.EnumOpening(enumType)#> 197 | { 198 | <# 199 | var foundOne = false; 200 | 201 | foreach (MetadataItem member in typeMapper.GetEnumMembers(enumType)) 202 | { 203 | foundOne = true; 204 | #> 205 | <#=code.Escape(typeMapper.GetEnumMemberName(member))#> = <#=typeMapper.GetEnumMemberValue(member)#>, 206 | <# 207 | } 208 | 209 | if (foundOne) 210 | { 211 | this.GenerationEnvironment.Remove(this.GenerationEnvironment.Length - 3, 1); 212 | } 213 | #> 214 | } 215 | <# 216 | EndNamespace(code); 217 | } 218 | 219 | fileManager.Process(); 220 | 221 | #> 222 | <#+ 223 | 224 | public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager) 225 | { 226 | fileManager.StartHeader(); 227 | #> 228 | //------------------------------------------------------------------------------ 229 | // 230 | // <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#> 231 | // 232 | // <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#> 233 | // <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine3")#> 234 | // 235 | //------------------------------------------------------------------------------ 236 | <#=codeStringGenerator.UsingDirectives(inHeader: true)#> 237 | <#+ 238 | fileManager.EndBlock(); 239 | } 240 | 241 | public void BeginNamespace(CodeGenerationTools code) 242 | { 243 | var codeNamespace = code.VsNamespaceSuggestion(); 244 | if (!String.IsNullOrEmpty(codeNamespace)) 245 | { 246 | #> 247 | namespace <#=code.EscapeNamespace(codeNamespace)#> 248 | { 249 | <#+ 250 | PushIndent(" "); 251 | } 252 | } 253 | 254 | public void EndNamespace(CodeGenerationTools code) 255 | { 256 | if (!String.IsNullOrEmpty(code.VsNamespaceSuggestion())) 257 | { 258 | PopIndent(); 259 | #> 260 | } 261 | <#+ 262 | } 263 | } 264 | 265 | public const string TemplateId = "CSharp_DbContext_Types_EF6"; 266 | 267 | public class CodeStringGenerator 268 | { 269 | private readonly CodeGenerationTools _code; 270 | private readonly TypeMapper _typeMapper; 271 | private readonly MetadataTools _ef; 272 | 273 | public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef) 274 | { 275 | ArgumentNotNull(code, "code"); 276 | ArgumentNotNull(typeMapper, "typeMapper"); 277 | ArgumentNotNull(ef, "ef"); 278 | 279 | _code = code; 280 | _typeMapper = typeMapper; 281 | _ef = ef; 282 | } 283 | 284 | public string Property(EdmProperty edmProperty) 285 | { 286 | return string.Format( 287 | CultureInfo.InvariantCulture, 288 | "{0} {1} {2} {{ {3}get; {4}set; }}", 289 | Accessibility.ForProperty(edmProperty), 290 | _typeMapper.GetTypeName(edmProperty.TypeUsage), 291 | _code.Escape(edmProperty), 292 | _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), 293 | _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); 294 | } 295 | 296 | public string NavigationProperty(NavigationProperty navProp) 297 | { 298 | var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType()); 299 | return string.Format( 300 | CultureInfo.InvariantCulture, 301 | "{0} {1} {2} {{ {3}get; {4}set; }}", 302 | AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)), 303 | navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, 304 | _code.Escape(navProp), 305 | _code.SpaceAfter(Accessibility.ForGetter(navProp)), 306 | _code.SpaceAfter(Accessibility.ForSetter(navProp))); 307 | } 308 | 309 | public string AccessibilityAndVirtual(string accessibility) 310 | { 311 | return accessibility + (accessibility != "private" ? " virtual" : ""); 312 | } 313 | 314 | public string EntityClassOpening(EntityType entity) 315 | { 316 | return string.Format( 317 | CultureInfo.InvariantCulture, 318 | "{0} {1}partial class {2}{3}", 319 | Accessibility.ForType(entity), 320 | _code.SpaceAfter(_code.AbstractOption(entity)), 321 | _code.Escape(entity), 322 | _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); 323 | } 324 | 325 | public string EnumOpening(SimpleType enumType) 326 | { 327 | return string.Format( 328 | CultureInfo.InvariantCulture, 329 | "{0} enum {1} : {2}", 330 | Accessibility.ForType(enumType), 331 | _code.Escape(enumType), 332 | _code.Escape(_typeMapper.UnderlyingClrType(enumType))); 333 | } 334 | 335 | public void WriteFunctionParameters(EdmFunction edmFunction, Action writeParameter) 336 | { 337 | var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); 338 | foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable)) 339 | { 340 | var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null"; 341 | var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + parameter.FunctionParameterName + ")"; 342 | var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + TypeMapper.FixNamespaces(parameter.RawClrTypeName) + "))"; 343 | writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit); 344 | } 345 | } 346 | 347 | public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace) 348 | { 349 | var parameters = _typeMapper.GetParameters(edmFunction); 350 | 351 | return string.Format( 352 | CultureInfo.InvariantCulture, 353 | "{0} IQueryable<{1}> {2}({3})", 354 | AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), 355 | _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), 356 | _code.Escape(edmFunction), 357 | string.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray())); 358 | } 359 | 360 | public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace) 361 | { 362 | var parameters = _typeMapper.GetParameters(edmFunction); 363 | 364 | return string.Format( 365 | CultureInfo.InvariantCulture, 366 | "return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});", 367 | _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), 368 | edmFunction.NamespaceName, 369 | edmFunction.Name, 370 | string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()), 371 | _code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()))); 372 | } 373 | 374 | public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) 375 | { 376 | var parameters = _typeMapper.GetParameters(edmFunction); 377 | var returnType = _typeMapper.GetReturnType(edmFunction); 378 | 379 | var paramList = String.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray()); 380 | if (includeMergeOption) 381 | { 382 | paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption"; 383 | } 384 | 385 | return string.Format( 386 | CultureInfo.InvariantCulture, 387 | "{0} {1} {2}({3})", 388 | AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), 389 | returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", 390 | _code.Escape(edmFunction), 391 | paramList); 392 | } 393 | 394 | public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) 395 | { 396 | var parameters = _typeMapper.GetParameters(edmFunction); 397 | var returnType = _typeMapper.GetReturnType(edmFunction); 398 | 399 | var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())); 400 | if (includeMergeOption) 401 | { 402 | callParams = ", mergeOption" + callParams; 403 | } 404 | 405 | return string.Format( 406 | CultureInfo.InvariantCulture, 407 | "return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});", 408 | returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", 409 | edmFunction.Name, 410 | callParams); 411 | } 412 | 413 | public string DbSet(EntitySet entitySet) 414 | { 415 | return string.Format( 416 | CultureInfo.InvariantCulture, 417 | "{0} virtual DbSet<{1}> {2} {{ get; set; }}", 418 | Accessibility.ForReadOnlyProperty(entitySet), 419 | _typeMapper.GetTypeName(entitySet.ElementType), 420 | _code.Escape(entitySet)); 421 | } 422 | 423 | public string UsingDirectives(bool inHeader, bool includeCollections = true) 424 | { 425 | return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) 426 | ? string.Format( 427 | CultureInfo.InvariantCulture, 428 | "{0}using System;{1}" + 429 | "{2}", 430 | inHeader ? Environment.NewLine : "", 431 | includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "", 432 | inHeader ? "" : Environment.NewLine) 433 | : ""; 434 | } 435 | } 436 | 437 | public class TypeMapper 438 | { 439 | private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; 440 | 441 | private readonly System.Collections.IList _errors; 442 | private readonly CodeGenerationTools _code; 443 | private readonly MetadataTools _ef; 444 | 445 | public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors) 446 | { 447 | ArgumentNotNull(code, "code"); 448 | ArgumentNotNull(ef, "ef"); 449 | ArgumentNotNull(errors, "errors"); 450 | 451 | _code = code; 452 | _ef = ef; 453 | _errors = errors; 454 | } 455 | 456 | public static string FixNamespaces(string typeName) 457 | { 458 | return typeName.Replace("System.Data.Spatial.", "System.Data.Entity.Spatial."); 459 | } 460 | 461 | public string GetTypeName(TypeUsage typeUsage) 462 | { 463 | return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null); 464 | } 465 | 466 | public string GetTypeName(EdmType edmType) 467 | { 468 | return GetTypeName(edmType, isNullable: null, modelNamespace: null); 469 | } 470 | 471 | public string GetTypeName(TypeUsage typeUsage, string modelNamespace) 472 | { 473 | return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace); 474 | } 475 | 476 | public string GetTypeName(EdmType edmType, string modelNamespace) 477 | { 478 | return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace); 479 | } 480 | 481 | public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace) 482 | { 483 | if (edmType == null) 484 | { 485 | return null; 486 | } 487 | 488 | var collectionType = edmType as CollectionType; 489 | if (collectionType != null) 490 | { 491 | return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace)); 492 | } 493 | 494 | var typeName = _code.Escape(edmType.MetadataProperties 495 | .Where(p => p.Name == ExternalTypeNameAttributeName) 496 | .Select(p => (string)p.Value) 497 | .FirstOrDefault()) 498 | ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ? 499 | _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) : 500 | _code.Escape(edmType)); 501 | 502 | if (edmType is StructuralType) 503 | { 504 | return typeName; 505 | } 506 | 507 | if (edmType is SimpleType) 508 | { 509 | var clrType = UnderlyingClrType(edmType); 510 | if (!IsEnumType(edmType)) 511 | { 512 | typeName = _code.Escape(clrType); 513 | } 514 | 515 | typeName = FixNamespaces(typeName); 516 | 517 | return clrType.IsValueType && isNullable == true ? 518 | String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) : 519 | typeName; 520 | } 521 | 522 | throw new ArgumentException("edmType"); 523 | } 524 | 525 | public Type UnderlyingClrType(EdmType edmType) 526 | { 527 | ArgumentNotNull(edmType, "edmType"); 528 | 529 | var primitiveType = edmType as PrimitiveType; 530 | if (primitiveType != null) 531 | { 532 | return primitiveType.ClrEquivalentType; 533 | } 534 | 535 | if (IsEnumType(edmType)) 536 | { 537 | return GetEnumUnderlyingType(edmType).ClrEquivalentType; 538 | } 539 | 540 | return typeof(object); 541 | } 542 | 543 | public object GetEnumMemberValue(MetadataItem enumMember) 544 | { 545 | ArgumentNotNull(enumMember, "enumMember"); 546 | 547 | var valueProperty = enumMember.GetType().GetProperty("Value"); 548 | return valueProperty == null ? null : valueProperty.GetValue(enumMember, null); 549 | } 550 | 551 | public string GetEnumMemberName(MetadataItem enumMember) 552 | { 553 | ArgumentNotNull(enumMember, "enumMember"); 554 | 555 | var nameProperty = enumMember.GetType().GetProperty("Name"); 556 | return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null); 557 | } 558 | 559 | public System.Collections.IEnumerable GetEnumMembers(EdmType enumType) 560 | { 561 | ArgumentNotNull(enumType, "enumType"); 562 | 563 | var membersProperty = enumType.GetType().GetProperty("Members"); 564 | return membersProperty != null 565 | ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null) 566 | : Enumerable.Empty(); 567 | } 568 | 569 | public bool EnumIsFlags(EdmType enumType) 570 | { 571 | ArgumentNotNull(enumType, "enumType"); 572 | 573 | var isFlagsProperty = enumType.GetType().GetProperty("IsFlags"); 574 | return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null); 575 | } 576 | 577 | public bool IsEnumType(GlobalItem edmType) 578 | { 579 | ArgumentNotNull(edmType, "edmType"); 580 | 581 | return edmType.GetType().Name == "EnumType"; 582 | } 583 | 584 | public PrimitiveType GetEnumUnderlyingType(EdmType enumType) 585 | { 586 | ArgumentNotNull(enumType, "enumType"); 587 | 588 | return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null); 589 | } 590 | 591 | public string CreateLiteral(object value) 592 | { 593 | if (value == null || value.GetType() != typeof(TimeSpan)) 594 | { 595 | return _code.CreateLiteral(value); 596 | } 597 | 598 | return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks); 599 | } 600 | 601 | public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable types, string sourceFile) 602 | { 603 | ArgumentNotNull(types, "types"); 604 | ArgumentNotNull(sourceFile, "sourceFile"); 605 | 606 | var hash = new HashSet(StringComparer.InvariantCultureIgnoreCase); 607 | if (types.Any(item => !hash.Add(item))) 608 | { 609 | _errors.Add( 610 | new CompilerError(sourceFile, -1, -1, "6023", 611 | String.Format(CultureInfo.CurrentCulture, CodeGenerationTools.GetResourceString("Template_CaseInsensitiveTypeConflict")))); 612 | return false; 613 | } 614 | return true; 615 | } 616 | 617 | public IEnumerable GetEnumItemsToGenerate(IEnumerable itemCollection) 618 | { 619 | return GetItemsToGenerate(itemCollection) 620 | .Where(e => IsEnumType(e)); 621 | } 622 | 623 | public IEnumerable GetItemsToGenerate(IEnumerable itemCollection) where T: EdmType 624 | { 625 | return itemCollection 626 | .OfType() 627 | .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) 628 | .OrderBy(i => i.Name); 629 | } 630 | 631 | public IEnumerable GetAllGlobalItems(IEnumerable itemCollection) 632 | { 633 | return itemCollection 634 | .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i)) 635 | .Select(g => GetGlobalItemName(g)); 636 | } 637 | 638 | public string GetGlobalItemName(GlobalItem item) 639 | { 640 | if (item is EdmType) 641 | { 642 | return ((EdmType)item).Name; 643 | } 644 | else 645 | { 646 | return ((EntityContainer)item).Name; 647 | } 648 | } 649 | 650 | public IEnumerable GetSimpleProperties(EntityType type) 651 | { 652 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); 653 | } 654 | 655 | public IEnumerable GetSimpleProperties(ComplexType type) 656 | { 657 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); 658 | } 659 | 660 | public IEnumerable GetComplexProperties(EntityType type) 661 | { 662 | return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); 663 | } 664 | 665 | public IEnumerable GetComplexProperties(ComplexType type) 666 | { 667 | return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); 668 | } 669 | 670 | public IEnumerable GetPropertiesWithDefaultValues(EntityType type) 671 | { 672 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); 673 | } 674 | 675 | public IEnumerable GetPropertiesWithDefaultValues(ComplexType type) 676 | { 677 | return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); 678 | } 679 | 680 | public IEnumerable GetNavigationProperties(EntityType type) 681 | { 682 | return type.NavigationProperties.Where(np => np.DeclaringType == type); 683 | } 684 | 685 | public IEnumerable GetCollectionNavigationProperties(EntityType type) 686 | { 687 | return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many); 688 | } 689 | 690 | public FunctionParameter GetReturnParameter(EdmFunction edmFunction) 691 | { 692 | ArgumentNotNull(edmFunction, "edmFunction"); 693 | 694 | var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters"); 695 | return returnParamsProperty == null 696 | ? edmFunction.ReturnParameter 697 | : ((IEnumerable)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault(); 698 | } 699 | 700 | public bool IsComposable(EdmFunction edmFunction) 701 | { 702 | ArgumentNotNull(edmFunction, "edmFunction"); 703 | 704 | var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute"); 705 | return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null); 706 | } 707 | 708 | public IEnumerable GetParameters(EdmFunction edmFunction) 709 | { 710 | return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); 711 | } 712 | 713 | public TypeUsage GetReturnType(EdmFunction edmFunction) 714 | { 715 | var returnParam = GetReturnParameter(edmFunction); 716 | return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage); 717 | } 718 | 719 | public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption) 720 | { 721 | var returnType = GetReturnType(edmFunction); 722 | return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType; 723 | } 724 | } 725 | 726 | public static void ArgumentNotNull(T arg, string name) where T : class 727 | { 728 | if (arg == null) 729 | { 730 | throw new ArgumentNullException(name); 731 | } 732 | } 733 | #> -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/User.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated from a template. 4 | // 5 | // Manual changes to this file may cause unexpected behavior in your application. 6 | // Manual changes to this file will be overwritten if the code is regenerated. 7 | // 8 | //------------------------------------------------------------------------------ 9 | 10 | namespace ThemeBot 11 | { 12 | using System; 13 | using System.Collections.Generic; 14 | 15 | public partial class User 16 | { 17 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] 18 | public User() 19 | { 20 | this.Themes = new HashSet(); 21 | this.Downloads = new HashSet(); 22 | this.Ratings = new HashSet(); 23 | } 24 | 25 | public int Id { get; set; } 26 | public int TelegramID { get; set; } 27 | public string Name { get; set; } 28 | public string Username { get; set; } 29 | public Nullable AccessFlags { get; set; } 30 | 31 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 32 | public virtual ICollection Themes { get; set; } 33 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 34 | public virtual ICollection Downloads { get; set; } 35 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] 36 | public virtual ICollection Ratings { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ThemeBot/ThemeBot/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | --------------------------------------------------------------------------------