public Command Resolve(string text) { try { var commands = Enum.GetValues(typeof(CommandType)); foreach (var cmd in commands) { var cmdEnum = ((CommandType)cmd); var words = cmdEnum.ToWords(); var matched = Match(text, words); if (matched) { return(new Command(cmdEnum, text)); } } // 時刻(hhmm)が入力されたら AnswerToEoW とする var hhmm = Hhmm.Parse(text); if (!hhmm.IsEmpty) { return(new Command(CommandType.AnswerToEoWWithTime, text)); } return(new Command(CommandType.None, text)); } catch (Exception ex) { Trace.WriteLine($"CommandResolver.Resolve failed. text: {text} - {ex.Message} - {ex.StackTrace}"); return(new Command(CommandType.None, text)); } }
public async Task ModifyTimecard(string yyyymmdd, string eoWTime) { var ymd = Yyyymmdd.Parse(yyyymmdd, _currentUser.TimeZoneId); if (ymd.isEmpty) { Trace.WriteLine($"ModifyTimecard parse ymd failed - {yyyymmdd}"); return; } // なしと言われたら該当日のタイムカードを削除 if (eoWTime.Contains("なし")) { await _monthlyTimecardRepo.DeleteTimecardRecord(_currentUser.UserId, ymd); // ユーザーのタイムゾーンでの現在時刻 var tzUser = TimeZoneInfo.FindSystemTimeZoneById(_currentUser.TimeZoneId); var nowUserTz = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzUser); var today = Yyyymmdd.FromDate(nowUserTz); // 削除日が今日だったら、また聞き始めるようにステータスを削除する if (ymd.Equals(today)) { var stateEntity = await _conversationStateRepo.GetStatusByUserId(_currentUser.UserId); if (stateEntity != null) { stateEntity.State = AskingState.None; await _conversationStateRepo.UpsertState(stateEntity); } } } else { // 該当日のタイムカードを更新または追加 var hhmm = Hhmm.Parse(eoWTime); if (hhmm.IsEmpty) { Trace.WriteLine($"ModifyTimecard parse hhmm failed - {hhmm}"); return; } await PunchEoW(ymd, hhmm); } }
public async Task <(Yyyymmdd ymd, Hhmm hm)> PunchEoW(string hhmmText) { var tzUser = TimeZoneInfo.FindSystemTimeZoneById(_currentUser.TimeZoneId); var nowUserTz = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzUser); // ユーザーのタイムゾーンでの現在時刻 var hhmm = Hhmm.Parse(hhmmText); var ymd = Yyyymmdd.FromDate(nowUserTz); // パース失敗していたら処理しない if (hhmm.IsEmpty || ymd.isEmpty) { Trace.WriteLine($"PunchEoW parse hhmm failed - {hhmmText}"); return(ymd, hhmm); } await PunchEoW(ymd, hhmm); return(ymd, hhmm); }
PunchEoW(ConversationStateEntity stateEntity) { var hhmm = Hhmm.Parse(stateEntity.TargetTime); // 該当日のタイムカードの終業時刻を更新 var ymd = Yyyymmdd.Parse(stateEntity.TargetDate, _currentUser.TimeZoneId); if (ymd.isEmpty || hhmm.IsEmpty) { Trace.WriteLine($"PunchEoW parse ymd, hhmm failed - {ymd}, {hhmm}"); return(ymd, hhmm); } var monthlyTimecardRepo = new MonthlyTimecardRepository(); await monthlyTimecardRepo.UpsertTimecardRecord(_currentUser.UserId, ymd, hhmm); // 打刻済みにして更新 stateEntity.State = AskingState.Punched; await _conversationStateRepo.UpsertState(stateEntity); return(ymd, hhmm); }
public void Send(bool disableFilter) { var appId = ConfigurationManager.AppSettings["MicrosoftAppId"]; var appPassword = ConfigurationManager.AppSettings["MicrosoftAppPassword"]; var nowUtc = DateTime.Now.ToUniversalTime(); Log($"UTC現在時刻- {nowUtc}"); var usersRepo = new UsersRepository(); var users = usersRepo.GetAllUsers().RunAsSync(); var conversationStateRepo = new ConversationStateRepository(); Log($"処理ユーザー数: {users.Count()}"); foreach (var user in users) { try { Log($"ユーザー: {user.NickName}({user.UserId}) ---"); var startHhmm = Hhmm.Parse(user.AskEndOfWorkStartTime); var endHhmm = Hhmm.Parse(user.AskEndOfWorkEndTime); // 24時超過分をオフセットして比較する // 19:00~26:00 の設定だった時に、翌日の深夜1時(25時)も送信対象となるように。 var offsetHour = endHhmm.Hour - 23; if (offsetHour < 0) { offsetHour = 0; } Log($"オフセット時間: {offsetHour}h"); var tzUser = TimeZoneInfo.FindSystemTimeZoneById(user.TimeZoneId); var nowUserTz = TimeZoneInfo.ConvertTimeFromUtc(nowUtc, tzUser); // ユーザーのタイムゾーンでの現在時刻 Log($"ユーザータイムゾーンの現在時刻(オフセット前): {nowUserTz}"); nowUserTz = nowUserTz.AddHours(-offsetHour); var nowHour = nowUserTz.Hour + offsetHour; var nowStepedMinute = nowUserTz.Minute / 30 * 30; Log($"ユーザータイムゾーンの現在時刻(オフセット前、丸め後): {nowHour}時{nowStepedMinute:00}分"); var startTotalMinute = (startHhmm.Hour - offsetHour) * 60 + startHhmm.Minute; var endTotalMinute = (endHhmm.Hour - offsetHour) * 60 + endHhmm.Minute; var nowTotalMinute = (nowHour - offsetHour) * 60 + nowStepedMinute; Log($"ユーザータイムゾーンの現在時刻(オフセット後、丸め後): {(nowHour - offsetHour)}時{nowStepedMinute:00}分"); Log($"判定時刻範囲(オフセット前): {startHhmm.Hour}時{startHhmm.Minute:00}分~{endHhmm.Hour}時{endHhmm.Minute:00}分"); Log($"判定時刻範囲(オフセット後): {(startHhmm.Hour - offsetHour)}時{startHhmm.Minute:00}分~{(endHhmm.Hour - offsetHour)}時{endHhmm.Minute:00}分"); if (startTotalMinute >= endTotalMinute) { Log($"{user.UserId} は、開始時刻({user.AskEndOfWorkStartTime})と終了時刻({user.AskEndOfWorkEndTime})が逆転しているので何もしない。"); continue; } var nowUserTzDateText = $"{nowUserTz.Year:0000}/{nowUserTz.Month:00}/{nowUserTz.Day:00}"; // ユーザーTZ現在時刻を文字列化 var stateEntity = conversationStateRepo.GetStatusByUserId(user.UserId).RunAsSync(); var currentTargetDate = stateEntity?.TargetDate ?? "2000/01/01"; // ターゲット日付と現在時刻が同じで、 // 打刻済/今日はもう聞かないで/休日だったら何もしない var currentState = stateEntity?.State ?? AskingState.None; if (!disableFilter) { if (string.Equals(nowUserTzDateText, currentTargetDate) && (currentState == AskingState.DoNotAskToday || currentState == AskingState.Punched || currentState == AskingState.TodayIsOff)) { Log($"ターゲット日付({currentTargetDate})とユーザーTZ現在日付({nowUserTzDateText})が同じで、State が {currentState} なので何もしない"); continue; } // 今日の曜日はユーザー設定で有効か? var enableDayOfWeek = (user.DayOfWeekEnables?.Length ?? 0) - 1 > (int)nowUserTz.DayOfWeek ? (user.DayOfWeekEnables[(int)nowUserTz.DayOfWeek] == '1') : true; if (!enableDayOfWeek) { Log($"ユーザーTZ現在日付({nowUserTzDateText})の曜日は仕事が休みなので何もしない"); continue; } // FIXME 毎年ある祝日か、単発の休日かの管理が面倒なので、とりまオミットしておく //// 祝日か?(面倒だからJson文字列のまま検索しちゃう) //var isHoliday = user.HolidaysJson?.Contains($"\"{nowUserTz:M/d}\"") ?? false; // "6/1" みたいにダブルコートして検索すればいいっしょ //if (isHoliday) //{ // Log($"ユーザーTZ現在日付({nowUserTzText})の休日に設定されている何もしない"); // continue; //} var containsTimeRange = startTotalMinute <= nowTotalMinute && nowTotalMinute <= endTotalMinute; // 聞き取り終了時刻を過ぎていたらStateをNoneにする // AskingEoW のまま y を打たれると打刻できてしまうので。 if (startTotalMinute > endTotalMinute) { conversationStateRepo.UpsertState( user.PartitionKey, user.UserId, AskingState.None, $"{endHhmm.Hour:00}{endHhmm.Minute:00}", nowUserTzDateText).RunAsSync(); } if (!containsTimeRange) { Log($"現在時刻({nowUserTz}) が {user.AskEndOfWorkStartTime} から {user.AskEndOfWorkEndTime} の範囲外なので何もしない"); continue; } } var conversationRef = JsonConvert.DeserializeObject <ConversationReference>(user.ConversationRef); MicrosoftAppCredentials.TrustServiceUrl(conversationRef.ServiceUrl); var connector = new ConnectorClient(new Uri(conversationRef.ServiceUrl), appId, appPassword); var userAccount = new ChannelAccount(id: user.UserId); var res = connector.Conversations.CreateDirectConversation(conversationRef.Bot, userAccount); // conversationRef.GetPostToUserMessage() では Slack にポストできなかったので、 // 普通に CreateMessageActivity した。 var message = Activity.CreateMessageActivity(); message.From = conversationRef.Bot; message.Recipient = userAccount; message.Conversation = new ConversationAccount(id: res.Id); message.Text = $"{user.NickName} さん、お疲れさまです。{nowHour}時{nowStepedMinute:00}分 です、今日のお仕事は終わりましたか?\n\n" + $"--\n\ny:終わった\n\nn:終わってない\n\nd:今日は徹夜"; message.Locale = "ja-Jp"; //message.Attachments.Add(new Attachment() //{ // ContentType = AdaptiveCard.ContentType, // Content = MakeAdaptiveCard() //}); connector.Conversations.SendToConversation((Activity)message); conversationStateRepo.UpsertState( user.PartitionKey, conversationRef.User.Id, AskingState.AskingEoW, $"{nowHour:00}{nowStepedMinute:00}", nowUserTzDateText).RunAsSync(); Log($"メッセージを送信しました。 ({message.Text})"); } catch (Exception ex) { Log($"メッセージ送信に失敗しました。 ({ex.Message} - {ex.StackTrace})"); } } }
private async Task ReceivedPreferenceTextAsync(IDialogContext context, IAwaitable <string> result) { var text = await result; var error = string.Empty; var cancelTerms = CommandType.Cancel.ToWords(); if (cancelTerms.Any(x => string.Equals(x, text))) { context.Fail(new OperationCanceledException($"Cancel by user - {text}")); return; } switch (_prefType) { case UserPreferenceType.NickName: break; case UserPreferenceType.EndOfWorkTime: case UserPreferenceType.EndOfConfirmTime: { // hhmm のバリデーション var hhmm = Hhmm.Parse(text); if (hhmm.IsEmpty) { error = "時刻は hhmm の形式で入力して下さい。"; } } break; case UserPreferenceType.DayOfWeekEnables: { // 休みの曜日のバリデーション if (!text.All(x => User.WEEKDAYS.Contains(x.ToString()))) { error = "休みの曜日は「月火水木金土日」から「土日」、「水金日」などの形式で入力して下さい。"; } } break; case UserPreferenceType.TimeZoneId: { // タイムゾーンのバリデーション try { TimeZoneInfo.FindSystemTimeZoneById(text); } catch (Exception) { error = "有効なタイムゾーンではありません。"; } } break; default: break; } if (string.IsNullOrEmpty(error)) { // 値を返す context.Done(new Result(_prefType, text)); } else { // 再度入力させる await context.PostAsync(error); await CommandPreferenceMenu(context, _prefType); } }