private bool UpdateLastPosted(string groupName, long id) { if (!LastPostedDictionary.ContainsKey(groupName)) { LastPostedDictionary.AddOrUpdateEx(groupName, 0); } if (LastPostedDictionary[groupName] == id) { return(true); } LastPostedDictionary[groupName] = id; return(false); }
public override async Task Run(object prm) { if (IsRunning || !APIHelper.IsDiscordAvailable) { return; } IsRunning = true; try { if ((DateTime.Now - _lastCheckTime).TotalMinutes < _checkInterval) { return; } _lastCheckTime = DateTime.Now; await LogHelper.LogModule("Running Mail module check...", Category); foreach (var(groupName, group) in Settings.MailModule.GetEnabledGroups()) { if (group.DefaultChannel == 0) { continue; } var defaultChannel = group.DefaultChannel; var chars = GetParsedCharacters(groupName) ?? new List <long>(); foreach (var charId in chars) { if (charId == 0) { continue; } var rToken = await SQLHelper.GetRefreshTokenMail(charId); if (string.IsNullOrEmpty(rToken)) { await SendOneTimeWarning(charId, $"Mail feed token for character {charId} not found! User is not authenticated."); continue; } var tq = await APIHelper.ESIAPI.RefreshToken(rToken, Settings.WebServerModule.CcpAppClientId, Settings.WebServerModule.CcpAppSecret , $"From {Category} | Char ID: {charId}"); var token = tq.Result; if (string.IsNullOrEmpty(token)) { await LogHelper.LogWarning($"Unable to get contracts token for character {charId}. Refresh token might be outdated or no more valid {tq.Data.ErrorCode}({tq.Data.Message})", Category); continue; } var includePrivate = group.IncludePrivateMail; if (group.Filters.Values.All(a => a.FilterSenders.Count == 0 && a.FilterLabels.Count == 0 && a.FilterMailList.Count == 0)) { await LogHelper.LogWarning($"Mail feed for user {charId} has no labels, lists or senders configured!", Category); continue; } var labelsData = await APIHelper.ESIAPI.GetMailLabels(Reason, charId.ToString(), token); var searchLabels = labelsData?.labels.Where(a => a.name?.ToLower() != "sent" && a.name?.ToLower() != "received").ToList() ?? new List <JsonClasses.MailLabel>(); var mailLists = await APIHelper.ESIAPI.GetMailLists(Reason, charId, token); var etag = _tags.GetOrNull(charId); var result = await APIHelper.ESIAPI.GetMailHeaders(Reason, charId.ToString(), token, 0, etag); _tags.AddOrUpdateEx(charId, result.Data.ETag); if (result.Data.IsNotModified) { continue; } var mailsHeaders = result.Result; var lastMailId = await SQLHelper.GetLastMailId(charId); var prevMailId = lastMailId; if (lastMailId > 0) { mailsHeaders = mailsHeaders.Where(a => a.mail_id > lastMailId).OrderBy(a => a.mail_id).ToList(); } else { lastMailId = mailsHeaders.OrderBy(a => a.mail_id).LastOrDefault()?.mail_id ?? 0; mailsHeaders.Clear(); } foreach (var mailHeader in mailsHeaders) { try { if (mailHeader.mail_id <= lastMailId) { continue; } lastMailId = mailHeader.mail_id; if (!includePrivate && (mailHeader.recipients.Count(a => a.recipient_id == charId) > 0)) { continue; } foreach (var filter in group.Filters.Values) { //filter by senders if (filter.FilterSenders.Count > 0 && !filter.FilterSenders.Contains(mailHeader.from)) { continue; } //filter by labels var labelIds = searchLabels.Where(a => filter.FilterLabels.Contains(a.name)).Select(a => a.label_id).ToList(); if (labelIds.Count > 0 && !mailHeader.labels.Any(a => labelIds.Contains(a))) { continue; } //filter by mail lists var mailListIds = filter.FilterMailList.Count > 0 ? mailLists.Where(a => filter.FilterMailList.Any(b => a.name.Equals(b, StringComparison.OrdinalIgnoreCase))).Select(a => a.mailing_list_id) .ToList() : new List <long>(); if (mailListIds.Count > 0 && !mailHeader.recipients.Where(a => a.recipient_type == "mailing_list") .Any(a => mailListIds.Contains(a.recipient_id))) { continue; } var mail = await APIHelper.ESIAPI.GetMail(Reason, charId.ToString(), token, mailHeader.mail_id); // var labelNames = string.Join(",", mail.labels.Select(a => searchLabels.FirstOrDefault(l => l.label_id == a)?.name)).Trim(','); var sender = await APIHelper.ESIAPI.GetCharacterData(Reason, mail.from); var from = sender?.name; var ml = mailHeader.recipients.FirstOrDefault(a => a.recipient_type == "mailing_list" && mailListIds.Contains(a.recipient_id)); if (ml != null) { from = $"{sender?.name}[{mailLists.First(a => a.mailing_list_id == ml.recipient_id).name}]"; } var channel = filter.FeedChannel > 0 ? filter.FeedChannel : defaultChannel; await SendMailNotification(channel, mail, LM.Get("mailMsgTitle", from), group.DefaultMention, filter.DisplayDetailsSummary); break; } } catch (Exception ex) { await LogHelper.LogEx($"MailCheck: {mailHeader?.mail_id} {mailHeader?.subject}", ex); } } if (prevMailId != lastMailId || lastMailId == 0) { await SQLHelper.UpdateMail(charId, lastMailId); } } } // await LogHelper.LogModule("Completed", Category); } catch (Exception ex) { await LogHelper.LogEx(ex.Message, ex, Category); // await LogHelper.LogModule("Completed", Category); } finally { IsRunning = false; } }
private async Task ProcessContracts(bool isCorp, ContractNotifyGroup group, long characterID, string token) { if (group == null) { return; } var maxContracts = Settings.ContractNotificationsModule.MaxTrackingCount > 0 ? Settings.ContractNotificationsModule.MaxTrackingCount : 150; List <JsonClasses.Contract> contracts; var corpID = isCorp ? (await APIHelper.ESIAPI.GetCharacterData(Reason, characterID))?.corporation_id ?? 0 : 0; if (isCorp) { var etag = _corpEtokens.GetOrNull(characterID); var result = await APIHelper.ESIAPI.GetCorpContracts(Reason, corpID, token, etag); if (result?.Data == null || result.Data.IsNotModified) { return; } _corpEtokens.AddOrUpdateEx(characterID, result.Data.ETag); contracts = result.Result?.OrderByDescending(a => a.contract_id).ToList(); } else { var etag = _etokens.GetOrNull(characterID); var result = await APIHelper.ESIAPI.GetCharacterContracts(Reason, characterID, token, etag); if (result?.Data == null || result.Data.IsNotModified) { return; } _etokens.AddOrUpdateEx(characterID, result.Data.ETag); contracts = result.Result?.OrderByDescending(a => a.contract_id).ToList(); } if (contracts == null || !contracts.Any()) { return; } var lastContractId = contracts.FirstOrDefault()?.contract_id ?? 0; if (lastContractId == 0) { return; } var lst = !isCorp ? await SQLHelper.LoadContracts(characterID, false) : await SQLHelper.LoadContracts(characterID, true); var otherList = isCorp ? await SQLHelper.LoadContracts(characterID, false) : null; if (lst == null) { lst = new List <JsonClasses.Contract>(contracts.Where(a => _activeStatuses.ContainsCaseInsensitive(a.status)).TakeSmart(maxContracts)); await SQLHelper.SaveContracts(characterID, lst, isCorp); return; } /* * if (lst == null) * { * var cs = contracts.Where(a => !_completeStatuses.Contains(a.status)).TakeSmart(maxContracts).ToList(); * //initial cache - only progressing contracts * await SQLHelper.SaveContracts(characterID, cs, isCorp); * return; * }*/ //process cache foreach (var contract in lst.ToList()) { var freshContract = contracts.FirstOrDefault(a => a.contract_id == contract.contract_id); //check if it present if (freshContract == null) { lst.Remove(contract); continue; } if (group.Filters == null) { continue; } foreach (var filter in group.Filters.Values) { if (filter.Types.Any() && !filter.Types.Contains(contract.type)) { continue; } if (filter.Availability.Any() && !filter.Availability.ContainsCaseInsensitive(contract.availability)) { continue; } //check for completion if (_completeStatuses.Contains(freshContract.status) && filter.Statuses.Contains(freshContract.status)) { if (filter.DiscordChannelId > 0 && APIHelper.DiscordAPI.GetChannel(filter.DiscordChannelId) != null) { await PrepareFinishedDiscordMessage(filter.DiscordChannelId, freshContract, group.DefaultMention, isCorp, characterID, corpID, token, filter); } else { await LogHelper.LogWarning($"Specified filter channel ID: {filter.DiscordChannelId} is not accessible!", Category); } await LogHelper.LogModule($"--> Contract {freshContract.contract_id} is {freshContract.status}!", Category); if (lst.Contains(contract)) { lst.Remove(contract); } continue; } //check for accepted if (contract.type == "courier" && contract.status == "outstanding" && freshContract.status == "in_progress" && filter.Statuses.Contains("in_progress")) { await PrepareAcceptedDiscordMessage(filter.DiscordChannelId, freshContract, group.DefaultMention, isCorp, characterID, corpID, token, filter); var index = lst.IndexOf(contract); lst.Remove(contract); lst.Insert(index < 0 ? 0 : index, freshContract); await LogHelper.LogModule($"--> Contract {freshContract.contract_id} is accepted!", Category); continue; } } } //silently remove filtered out expired contracts var lefties = lst.Where(a => _completeStatuses.Contains(a.status)).ToList(); foreach (var lefty in lefties) { lst.Remove(lefty); } //update cache list and look for new contracts var lastRememberedId = lst.FirstOrDefault()?.contract_id ?? 0; if (lastContractId > lastRememberedId) { //get and report new contracts, forget already finished var list = contracts.Where(a => a.contract_id > lastRememberedId && !_completeStatuses.Contains(a.status)).ToList(); if (otherList != null) { list = list.Where(a => otherList.All(b => b.contract_id != a.contract_id)).ToList(); } //fix loop foreach (var contract in list) { var isCharAssignee = await APIHelper.ESIAPI.GetCharacterData(Reason, contract.assignee_id) != null; bool isCorpAssignee = false; bool isAllyAssignee = false; if (!isCharAssignee) { isCorpAssignee = await APIHelper.ESIAPI.GetCorporationData(Reason, contract.assignee_id) != null; isAllyAssignee = !isCorpAssignee; } contract.availability = isCharAssignee ? "personal" : (isCorpAssignee ? "corporation" : "alliance"); } bool stop = false; foreach (var contract in list) { foreach (var(filterName, filter) in group.Filters) { if (stop) { break; } if (!filter.Statuses.Contains(contract.status)) { continue; } //types if (filter.Types.Any() && !filter.Types.Contains(contract.type)) { continue; } //availability if (filter.Availability.Any() && !filter.Availability.ContainsCaseInsensitive(contract.availability)) { continue; } //filter by issue target if (!filter.FeedIssuedBy) { if (isCorp) { if (contract.for_corporation && contract.issuer_corporation_id == corpID) { continue; } } else { if (contract.issuer_id == characterID) { continue; } } } if (!filter.FeedIssuedTo) { if (isCorp) { if (contract.assignee_id == corpID) { continue; } else if (contract.assignee_id == characterID) { continue; } } } try { await LogHelper.LogModule($"--> New Contract {contract.contract_id} found!", Category); if (filter.DiscordChannelId != 0) { await PrepareDiscordMessage(filter.DiscordChannelId, contract, group.DefaultMention, isCorp, characterID, corpID, token, filter); } if (group.StopOnFirstFilterMatch) { stop = true; } } catch (Exception ex) { await LogHelper.LogEx($"Contract {contract.contract_id}", ex, Category); } } } if (list.Count > 0) { lst.InsertRange(0, list); //cut if (lst.Count >= maxContracts) { var count = lst.Count - maxContracts; lst.RemoveRange(lst.Count - count, count); } } } //kill dupes var rr = lst.GroupBy(a => a.contract_id).Where(a => a.Count() > 1).Select(a => a.Key).Distinct(); foreach (var item in rr) { var o = lst.FirstOrDefault(a => a.contract_id == item); if (o != null) { lst.Remove(o); } } await SQLHelper.SaveContracts(characterID, lst, isCorp); }
private async Task ProcessIndustryJobs(bool isCorp, IndustrialJobGroup group, long characterID, string token) { if (group == null) { return; } List <JsonClasses.IndustryJob> esiJobs; var corpID = isCorp ? (await APIHelper.ESIAPI.GetCharacterData(Reason, characterID))?.corporation_id ?? 0 : 0; if (isCorp) { var etag = _corpEtokens.GetOrNull(characterID); var result = await APIHelper.ESIAPI.GetCorpIndustryJobs(Reason, corpID, token, etag); _corpEtokens.AddOrUpdateEx(characterID, result?.Data?.ETag); if (result?.Data?.IsNotModified ?? true) { return; } esiJobs = result.Result?.OrderByDescending(a => a.job_id).ToList(); } else { var etag = _etokens.GetOrNull(characterID); var result = await APIHelper.ESIAPI.GetCharacterIndustryJobs(Reason, characterID, token, etag); _etokens.AddOrUpdateEx(characterID, result?.Data?.ETag); if (result?.Data?.IsNotModified ?? true) { return; } esiJobs = result.Result?.OrderByDescending(a => a.job_id).ToList(); } if (esiJobs == null || !esiJobs.Any()) { return; } //ccp bug workaround var now = DateTime.UtcNow; foreach (var job in esiJobs.Where(a => a.StatusValue == IndustryJobStatusEnum.active && a.end_date < now)) { job.status = "ready"; } var lastJobId = esiJobs.FirstOrDefault()?.job_id ?? 0; if (lastJobId == 0) { return; } var dbJobs = !isCorp ? await SQLHelper.LoadIndustryJobs(characterID, false) : await SQLHelper.LoadIndustryJobs(characterID, true); var dbJobsOther = isCorp ? await SQLHelper.LoadIndustryJobs(characterID, false) : null;//TODO check for sanity //check if initial startup if (dbJobs == null) { dbJobs = new List <JsonClasses.IndustryJob>(esiJobs.Where(a => a.StatusValue != IndustryJobStatusEnum.delivered)); //if (dbJobs.Any()) // { await SQLHelper.SaveIndustryJobs(characterID, dbJobs, isCorp); return; //} } // var ready = dbJobs.Where(a => a.StatusValue == IndustryJobStatusEnum.ready); // var ready2 = esiJobs.Where(a => a.StatusValue == IndustryJobStatusEnum.ready); //var x = dbJobs.FirstOrDefault(a => a.blueprint_type_id == 41607); //check db jobs foreach (var job in dbJobs.ToList()) { var freshJob = esiJobs.FirstOrDefault(a => a.job_id == job.job_id); //check if job is present in esi, delete from db if not if (freshJob == null) { dbJobs.Remove(job); continue; } var filters = group.Filters.Count == 0 ? new Dictionary <string, IndustryJobFilter> { { "default", new IndustryJobFilter() } } : group.Filters; foreach (var(filterName, filter) in filters) { if (!CheckJobForFilter(filter, job, isCorp)) { continue; } //check delivered if (freshJob.StatusValue != job.StatusValue) { await SendDiscordMessage(freshJob, true, filter.DiscordChannels.Any()?filter.DiscordChannels : @group.DiscordChannels, isCorp, token); var index = dbJobs.IndexOf(job); dbJobs.Remove(job); dbJobs.Insert(index, freshJob); } } //remove delivered job from db if (freshJob.StatusValue == IndustryJobStatusEnum.delivered) { dbJobs.Remove(job); } } //silently remove filtered out expired contracts //probably not needed... dbJobs.RemoveAll(a => a.StatusValue == IndustryJobStatusEnum.delivered); //update cache list and look for new contracts var lastRememberedId = dbJobs?.FirstOrDefault()?.job_id ?? 0; if (lastJobId > lastRememberedId) { //get and report new jobs, forget already finished var newJobs = esiJobs.Where(a => a.job_id > lastRememberedId && a.StatusValue != IndustryJobStatusEnum.delivered).ToList(); if (dbJobsOther != null) { newJobs = newJobs.Where(a => dbJobsOther.All(b => b.job_id != a.job_id)).ToList(); } //process new jobs foreach (var job in newJobs) { foreach (var(filterName, filter) in @group.Filters) { if (!CheckJobForFilter(filter, job, isCorp)) { continue; } await SendDiscordMessage(job, false, filter.DiscordChannels.Any()?filter.DiscordChannels : @group.DiscordChannels, isCorp, token); } } //add new jobs to db list if (newJobs.Count > 0) { dbJobs.InsertRange(0, newJobs); } } //kill dupes var rr = dbJobs.GroupBy(a => a.job_id).Where(a => a.Count() > 1).Select(a => a.Key).Distinct(); foreach (var item in rr) { var o = dbJobs.FirstOrDefault(a => a.job_id == item); if (o != null) { dbJobs.Remove(o); } } await SQLHelper.SaveIndustryJobs(characterID, dbJobs, isCorp); }
public override async Task Run(object prm) { if (IsRunning) { return; } IsRunning = true; await ProcessExistingCampaigns(); try { if (DateTime.Now <= _nextNotificationCheck) { return; } _nextNotificationCheck = DateTime.Now.AddMinutes(Settings.NullCampaignModule.CheckIntervalInMinutes); var etag = _tags.GetOrNull(0); var result = await APIHelper.ESIAPI.GetNullCampaigns(Reason, etag); _tags.AddOrUpdateEx(0, result.Data.ETag); if (result.Data.IsNotModified) { return; } var allCampaigns = result.Result; if (allCampaigns == null) { return; } foreach (var pair in Settings.NullCampaignModule.Groups) { var groupName = pair.Key; var group = pair.Value; var systems = new List <JsonClasses.SystemName>(); foreach (var regionId in @group.Regions) { systems.AddRange(await SQLHelper.GetSystemsByRegion(regionId)); } foreach (var cId in @group.Constellations) { systems.AddRange(await SQLHelper.GetSystemsByConstellation(cId)); } var systemIds = systems.Select(a => a.system_id); var campaigns = allCampaigns.Where(a => systemIds.Contains(a.solar_system_id)); var existIds = await SQLHelper.GetNullsecCampaignIdList(groupName); campaigns = campaigns.Where(a => !existIds.Contains(a.campaign_id)); foreach (var campaign in campaigns) { if (await SQLHelper.IsNullsecCampaignExists(groupName, campaign.campaign_id)) { continue; } var startTime = campaign.Time; var totalMinutes = DateTime.UtcNow >= startTime ? 0 : (int)(startTime - DateTime.UtcNow).TotalMinutes; if (totalMinutes == 0) { continue; } await SQLHelper.UpdateNullCampaign(groupName, campaign.campaign_id, startTime, campaign.ToJson()); if (group.ReportNewCampaign) { await PrepareMessage(campaign, pair.Value, LM.Get("NC_NewCampaign"), 0x00FF00); } await LogHelper.LogInfo($"Nullsec Campaign {campaign.campaign_id} has been registered! [{groupName} - {campaign.campaign_id}]", Category, true, false); } } } catch (Exception ex) { await LogHelper.LogEx(ex.Message, ex, Category); } finally { IsRunning = false; } }
private async Task ProcessContracts(bool isCorp, ContractNotifyGroup group, long characterID, string token) { var maxContracts = Settings.ContractNotificationsModule.MaxTrackingCount > 0 ? Settings.ContractNotificationsModule.MaxTrackingCount : 150; List <JsonClasses.Contract> contracts; var corpID = isCorp ? (await APIHelper.ESIAPI.GetCharacterData(Reason, characterID))?.corporation_id ?? 0 : 0; if (isCorp) { var etag = _corpEtokens.GetOrNull(characterID); var result = await APIHelper.ESIAPI.GetCorpContracts(Reason, corpID, token, etag); _corpEtokens.AddOrUpdateEx(characterID, result.Data.ETag); if (result.Data.IsNotModified) { return; } contracts = result.Result?.OrderByDescending(a => a.contract_id).ToList(); } else { var etag = _etokens.GetOrNull(characterID); var result = await APIHelper.ESIAPI.GetCharacterContracts(Reason, characterID, token, etag); _etokens.AddOrUpdateEx(characterID, result.Data.ETag); if (result.Data.IsNotModified) { return; } contracts = result.Result?.OrderByDescending(a => a.contract_id).ToList(); } if (contracts == null || !contracts.Any()) { return; } var lastContractId = contracts.FirstOrDefault()?.contract_id ?? 0; if (lastContractId == 0) { return; } var lst = !isCorp ? await SQLHelper.LoadContracts(characterID, false) : await SQLHelper.LoadContracts(characterID, true); var otherList = isCorp ? await SQLHelper.LoadContracts(characterID, false) : null; if (lst == null) { var cs = contracts.Where(a => !_completeStatuses.Contains(a.status)).TakeSmart(maxContracts).ToList(); //initial cache - only progressing contracts await SQLHelper.SaveContracts(characterID, cs, isCorp); return; } //process cache foreach (var contract in lst.ToList()) { var freshContract = contracts.FirstOrDefault(a => a.contract_id == contract.contract_id); //check if it present if (freshContract == null) { lst.Remove(contract); continue; } foreach (var filter in group.Filters.Values) { if (filter.Types.Any() && !filter.Types.Contains(contract.type)) { continue; } //check for completion if (_completeStatuses.Contains(freshContract.status) && filter.Statuses.Contains(freshContract.status)) { await PrepareFinishedDiscordMessage(filter.DiscordChannelId, freshContract, group.DefaultMention, isCorp, characterID, corpID, token, filter.ShowIngameOpen); await LogHelper.LogModule($"--> Contract {freshContract.contract_id} is expired!", Category); lst.Remove(contract); continue; } //check for accepted if (contract.type == "courier" && contract.status == "outstanding" && freshContract.status == "in_progress" && filter.Statuses.Contains("in_progress")) { await PrepareAcceptedDiscordMessage(filter.DiscordChannelId, freshContract, group.DefaultMention, isCorp, characterID, corpID, token, filter.ShowIngameOpen); var index = lst.IndexOf(contract); lst.Remove(contract); lst.Insert(index, freshContract); await LogHelper.LogModule($"--> Contract {freshContract.contract_id} is accepted!", Category); continue; } } //silently remove filtered out expired contracts var lefties = lst.Where(a => _completeStatuses.Contains(a.status)).ToList(); foreach (var lefty in lefties) { lst.Remove(lefty); } } //update cache list and look for new contracts var lastRememberedId = lst.FirstOrDefault()?.contract_id ?? 0; if (lastContractId > lastRememberedId) { //get and report new contracts, forget already finished var list = contracts.Where(a => a.contract_id > lastRememberedId && !_completeStatuses.Contains(a.status)).ToList(); if (otherList != null) { list = list.Where(a => otherList.All(b => b.contract_id != a.contract_id)).ToList(); } var crFilter = group.Filters.Values.FirstOrDefault(a => a.Statuses.Contains("outstanding")); var crFilterChannel = crFilter?.DiscordChannelId ?? 0; //filter by issue target if (!crFilter?.FeedIssuedBy ?? false) { list = list.Where(a => a.issuer_id != characterID && (a.issuer_corporation_id != corpID || a.issuer_corporation_id == 0)).ToList(); } if (!crFilter?.FeedIssuedTo ?? false) { list = list.Where(a => a.assignee_id != characterID && a.assignee_id != corpID).ToList(); } if (crFilter != null && crFilter.Types.Any()) { list = list.Where(a => crFilter.Types.Contains(a.type)).ToList(); } foreach (var contract in list) { try { await LogHelper.LogModule($"--> New Contract {contract.contract_id} found!", Category); if (crFilterChannel != 0) { await PrepareDiscordMessage(crFilterChannel, contract, group.DefaultMention, isCorp, characterID, corpID, token, crFilter.ShowIngameOpen); } } catch (Exception ex) { await LogHelper.LogEx($"Contract {contract.contract_id}", ex, Category); } } if (list.Count > 0) { lst.InsertRange(0, list); //cut if (lst.Count >= maxContracts) { var count = lst.Count - maxContracts; lst.RemoveRange(lst.Count - count, count); } } } await SQLHelper.SaveContracts(characterID, lst, isCorp); }