From 3149862ab290a0b0cadf171561514ca87e2cec38 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 16:06:20 -0300 Subject: [PATCH 01/60] feat: adicionar enum UserStatus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enum para controlar disponibilidade dos usuários (AVAILABLE, BUSY, UNAVAILABLE) --- src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt new file mode 100644 index 0000000..80e8955 --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt @@ -0,0 +1,7 @@ +package ufrpe.sbpc.botpcd.entity + +enum class UserStatus { + AVAILABLE, /* (DISPONIVEL) ele pode receber um atendimento */ + BUSY, /* (OCUPADO) ele esta em atendimento, mas quando finalizar o codigo trocara para disponivel */ + UNAVAILABLE /* (INDISPONIVEL) ele foi ao banheiro / esta ocupado. Nao vai receber chamado ate que altere seu status manualmente para available*/ +} \ No newline at end of file From 3cd6a18a9e99cf0260e298829676eb9ac97be70c Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 16:06:57 -0300 Subject: [PATCH 02/60] =?UTF-8?q?feat:=20criei=20um=20reposit=C3=B3rio=20p?= =?UTF-8?q?ara=20Attendance=20(atendimentos)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Interface básica JPA para operações CRUD de atendimentos --- .../ufrpe/sbpc/botpcd/repository/AttendanceRepository.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/repository/AttendanceRepository.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/repository/AttendanceRepository.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/AttendanceRepository.kt new file mode 100644 index 0000000..67c3dfc --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/AttendanceRepository.kt @@ -0,0 +1,6 @@ +package ufrpe.sbpc.botpcd.repository + +import org.springframework.data.jpa.repository.JpaRepository +import ufrpe.sbpc.botpcd.entity.Attendance + +interface AttendanceRepository : JpaRepository \ No newline at end of file From 75375d91c6461460d197b1739e6e900fe4f84d31 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 16:07:28 -0300 Subject: [PATCH 03/60] feat: adicionei endpoints REST para atendimentos Endpoints para iniciar, finalizar e listar atendimentos --- .../botpcd/controller/AttendanceController.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/controller/AttendanceController.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/AttendanceController.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/AttendanceController.kt new file mode 100644 index 0000000..75f2daa --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/AttendanceController.kt @@ -0,0 +1,19 @@ +package ufrpe.sbpc.botpcd.controller + +import org.springframework.web.bind.annotation.* +import ufrpe.sbpc.botpcd.entity.Attendance +import ufrpe.sbpc.botpcd.service.AttendanceService + +@RestController +@RequestMapping("/attendances") +class AttendanceController(private val attendanceService: AttendanceService) { + + @PostMapping("/begin") + fun begin(@RequestBody attendance: Attendance) = attendanceService.beginAttendance(attendance) + + @PostMapping("/end") + fun end(@RequestBody attendance: Attendance) = attendanceService.endAttendance(attendance) + + @GetMapping + fun list(): List = attendanceService.searchAttendances() +} \ No newline at end of file From 24257f3a605b44948b581ca39198ff07930d6f31 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 16:08:00 -0300 Subject: [PATCH 04/60] =?UTF-8?q?feat:=20criei=20servi=C3=A7o=20para=20ger?= =?UTF-8?q?enciamento=20de=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementa operações para alterar e consultar status de monitores e membros da comissão --- .../sbpc/botpcd/service/UserStatusService.kt | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/service/UserStatusService.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/UserStatusService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/UserStatusService.kt new file mode 100644 index 0000000..a62c128 --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/UserStatusService.kt @@ -0,0 +1,40 @@ +package ufrpe.sbpc.botpcd.service + +import org.springframework.stereotype.Service +import ufrpe.sbpc.botpcd.entity.* +import ufrpe.sbpc.botpcd.repository.MonitorRepository +import ufrpe.sbpc.botpcd.repository.CommitteeMemberRepository +import org.springframework.transaction.annotation.Transactional + +@Service +class UserStatusService( + private val monitorRepository: MonitorRepository, + private val committeeMemberRepository: CommitteeMemberRepository +) { + + @Transactional + fun setMonitorStatus(monitor: Monitor, status: UserStatus) { + monitor.status = status + monitorRepository.save(monitor) + } + + @Transactional + fun setCommitteeMemberStatus(member: CommitteeMember, status: UserStatus) { + member.status = status + committeeMemberRepository.save(member) + } + + fun findAvailableMonitors(): List { + return monitorRepository.findByStatus(UserStatus.AVAILABLE) + } + + fun findAvailableCommitteeMembers(): List { + return committeeMemberRepository.findByStatus(UserStatus.AVAILABLE) + } + + // Método para filtrar por tipo de assistência também + fun findAvailableMonitorsByType(assistanceType: MonitorAssistanceType): List { + val availableMonitors = findAvailableMonitors() + return availableMonitors.filter { it.assistanceTypes == assistanceType } + } +} \ No newline at end of file From 9721f39dffe4a6261d17165cd44e9c9c7aa32597 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 16:08:30 -0300 Subject: [PATCH 05/60] =?UTF-8?q?feat:=20adicionei=20endpoints=20para=20al?= =?UTF-8?q?tera=C3=A7=C3=A3o=20manual=20de=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permite que monitores e membros alterem seu status via API REST --- .../botpcd/controller/UserStatusController.kt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/controller/UserStatusController.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/UserStatusController.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/UserStatusController.kt new file mode 100644 index 0000000..5276115 --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/UserStatusController.kt @@ -0,0 +1,34 @@ +package ufrpe.sbpc.botpcd.controller + +import org.springframework.web.bind.annotation.* +import ufrpe.sbpc.botpcd.entity.UserStatus +import ufrpe.sbpc.botpcd.repository.MonitorRepository +import ufrpe.sbpc.botpcd.repository.CommitteeMemberRepository +import ufrpe.sbpc.botpcd.service.UserStatusService + +@RestController +@RequestMapping("/user-status") +class UserStatusController( + private val userStatusService: UserStatusService, + private val monitorRepository: MonitorRepository, + private val committeeMemberRepository: CommitteeMemberRepository +) { + + @PostMapping("/monitor/{id}") + fun updateMonitorStatus( + @PathVariable id: Long, + @RequestParam status: UserStatus + ) { + val monitor = monitorRepository.findById(id).orElseThrow() + userStatusService.setMonitorStatus(monitor, status) + } + + @PostMapping("/committee/{id}") + fun updateCommitteeMemberStatus( + @PathVariable id: Long, + @RequestParam status: UserStatus + ) { + val member = committeeMemberRepository.findById(id).orElseThrow() + userStatusService.setCommitteeMemberStatus(member, status) + } +} \ No newline at end of file From 6e05e2106f33633b346d8fb85d50e31d8a669e54 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 16:08:54 -0300 Subject: [PATCH 06/60] feat: adicionei campo status em CommitteeMember MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permite controlar disponibilidade dos membros da comissão --- src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt index a692723..c16fc44 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt @@ -1,6 +1,8 @@ package ufrpe.sbpc.botpcd.entity import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated /** * @@ -8,5 +10,7 @@ import jakarta.persistence.Entity @Entity class CommitteeMember( name: String, - phoneNumber: String + phoneNumber: String, + @Enumerated(EnumType.STRING) + var status: UserStatus = UserStatus.AVAILABLE ): User(name = name, phoneNumber = phoneNumber) \ No newline at end of file From d4969f477df69057db56a0f492e92bdf7149b7ab Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 16:09:10 -0300 Subject: [PATCH 07/60] feat: adicionei campo status em Monitor Permite controlar disponibilidade do monitor para novos atendimentos --- src/main/kotlin/ufrpe/sbpc/botpcd/entity/Monitor.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Monitor.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Monitor.kt index 1ebc1b7..1cd0c55 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Monitor.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Monitor.kt @@ -17,4 +17,6 @@ class Monitor( @Enumerated(value = EnumType.STRING) @NotEmpty(message = "Monitor needs to have an assistance type") var assistanceTypes: MonitorAssistanceType, + @Enumerated(EnumType.STRING) + var status: UserStatus = UserStatus.AVAILABLE ) : User(name = name, phoneNumber = phoneNumber) \ No newline at end of file From 771b219bcf5ccd880f4d75c7d527bf9d35990f13 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 16:10:21 -0300 Subject: [PATCH 08/60] feat: adicionei a busca de membros por status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permite filtrar membros da comissão disponíveis para atendimentos --- .../ufrpe/sbpc/botpcd/repository/CommitteeMemberRepository.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/repository/CommitteeMemberRepository.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/CommitteeMemberRepository.kt index 505e6ad..62302cf 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/repository/CommitteeMemberRepository.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/CommitteeMemberRepository.kt @@ -2,7 +2,9 @@ package ufrpe.sbpc.botpcd.repository import org.springframework.data.jpa.repository.JpaRepository import ufrpe.sbpc.botpcd.entity.CommitteeMember +import ufrpe.sbpc.botpcd.entity.UserStatus interface CommitteeMemberRepository: JpaRepository { fun findByPhoneNumber(phoneNumber: String): CommitteeMember? + fun findByStatus(status: UserStatus): List } \ No newline at end of file From 1f782830da787a1ed2b0ad2184f5a706bab89227 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 16:10:38 -0300 Subject: [PATCH 09/60] feat: adicionei a busca de monitores por status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Permite filtrar monitores disponíveis para alocação em atendimentos --- .../kotlin/ufrpe/sbpc/botpcd/repository/MonitorRepository.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MonitorRepository.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MonitorRepository.kt index f1e6cc0..a4984b4 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MonitorRepository.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MonitorRepository.kt @@ -2,7 +2,9 @@ package ufrpe.sbpc.botpcd.repository import org.springframework.data.jpa.repository.JpaRepository import ufrpe.sbpc.botpcd.entity.Monitor +import ufrpe.sbpc.botpcd.entity.UserStatus interface MonitorRepository : JpaRepository{ fun findByPhoneNumber(phoneNumber: String): Monitor? + fun findByStatus(status: UserStatus): List } \ No newline at end of file From 1b0d839b1174e237b583b6f8a3d2deb094d43228 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 16:11:48 -0300 Subject: [PATCH 10/60] =?UTF-8?q?feat:=20adicionei=20in=C3=ADcio=20e=20fim?= =?UTF-8?q?=20de=20atendimentos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementa início e fim de atendimentos com atualização automática de status, mas sem nada elaborado, apenas básico para usar o enum de UserStatus. --- .../sbpc/botpcd/service/AttendanceService.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt new file mode 100644 index 0000000..cd66cef --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt @@ -0,0 +1,31 @@ +package ufrpe.sbpc.botpcd.service + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import ufrpe.sbpc.botpcd.entity.* +import ufrpe.sbpc.botpcd.repository.AttendanceRepository + +@Service +class AttendanceService( + private val attendanceRepository: AttendanceRepository, + private val userStatusService: UserStatusService +) { + + fun searchAttendances(): List { + return attendanceRepository.findAll() + } + + @Transactional + fun beginAttendance(attendance: Attendance) { + userStatusService.setMonitorStatus(attendance.monitor, UserStatus.BUSY) + attendanceRepository.save(attendance) + } + + @Transactional + fun endAttendance(attendance: Attendance) { + userStatusService.setMonitorStatus(attendance.monitor, UserStatus.AVAILABLE) + attendance.endDateTime = java.time.LocalDateTime.now() + attendanceRepository.save(attendance) + } + +} \ No newline at end of file From 7b6c69168a6c80ddadf81f358bae53a724b69967 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 23:51:47 -0300 Subject: [PATCH 11/60] =?UTF-8?q?Apaguei=20os=20controllers=20desnecess?= =?UTF-8?q?=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Victor Yghor Simões dos Santos <124019555+victoryghor@users.noreply.github.com> --- .../botpcd/controller/AttendanceController.kt | 19 ----------- .../botpcd/controller/UserStatusController.kt | 34 ------------------- 2 files changed, 53 deletions(-) delete mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/controller/AttendanceController.kt delete mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/controller/UserStatusController.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/AttendanceController.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/AttendanceController.kt deleted file mode 100644 index 75f2daa..0000000 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/AttendanceController.kt +++ /dev/null @@ -1,19 +0,0 @@ -package ufrpe.sbpc.botpcd.controller - -import org.springframework.web.bind.annotation.* -import ufrpe.sbpc.botpcd.entity.Attendance -import ufrpe.sbpc.botpcd.service.AttendanceService - -@RestController -@RequestMapping("/attendances") -class AttendanceController(private val attendanceService: AttendanceService) { - - @PostMapping("/begin") - fun begin(@RequestBody attendance: Attendance) = attendanceService.beginAttendance(attendance) - - @PostMapping("/end") - fun end(@RequestBody attendance: Attendance) = attendanceService.endAttendance(attendance) - - @GetMapping - fun list(): List = attendanceService.searchAttendances() -} \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/UserStatusController.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/UserStatusController.kt deleted file mode 100644 index 5276115..0000000 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/UserStatusController.kt +++ /dev/null @@ -1,34 +0,0 @@ -package ufrpe.sbpc.botpcd.controller - -import org.springframework.web.bind.annotation.* -import ufrpe.sbpc.botpcd.entity.UserStatus -import ufrpe.sbpc.botpcd.repository.MonitorRepository -import ufrpe.sbpc.botpcd.repository.CommitteeMemberRepository -import ufrpe.sbpc.botpcd.service.UserStatusService - -@RestController -@RequestMapping("/user-status") -class UserStatusController( - private val userStatusService: UserStatusService, - private val monitorRepository: MonitorRepository, - private val committeeMemberRepository: CommitteeMemberRepository -) { - - @PostMapping("/monitor/{id}") - fun updateMonitorStatus( - @PathVariable id: Long, - @RequestParam status: UserStatus - ) { - val monitor = monitorRepository.findById(id).orElseThrow() - userStatusService.setMonitorStatus(monitor, status) - } - - @PostMapping("/committee/{id}") - fun updateCommitteeMemberStatus( - @PathVariable id: Long, - @RequestParam status: UserStatus - ) { - val member = committeeMemberRepository.findById(id).orElseThrow() - userStatusService.setCommitteeMemberStatus(member, status) - } -} \ No newline at end of file From a8091891048f4b0070a94267d4b967f5ecb65734 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Tue, 13 May 2025 23:52:10 -0300 Subject: [PATCH 12/60] =?UTF-8?q?Ajustei=20coment=C3=A1rios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Victor Yghor Simões dos Santos <124019555+victoryghor@users.noreply.github.com> --- .../kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt | 11 ++++++++--- src/main/resources/application.properties | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt index 80e8955..3b8544b 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt @@ -1,7 +1,12 @@ package ufrpe.sbpc.botpcd.entity +/* +(DISPONIVEL) ele pode receber um atendimento +(OCUPADO) ele esta em atendimento, mas quando finalizar o codigo trocara para disponivel +(INDISPONIVEL) ele foi ao banheiro / esta ocupado. Nao vai receber chamado ate que altere seu status manualmente para available +*/ enum class UserStatus { - AVAILABLE, /* (DISPONIVEL) ele pode receber um atendimento */ - BUSY, /* (OCUPADO) ele esta em atendimento, mas quando finalizar o codigo trocara para disponivel */ - UNAVAILABLE /* (INDISPONIVEL) ele foi ao banheiro / esta ocupado. Nao vai receber chamado ate que altere seu status manualmente para available*/ + AVAILABLE, + BUSY, + UNAVAILABLE } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index cdbbebf..4126a38 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1,3 @@ spring.application.name=BotPCD -whatsapp.token=ajskjfaiebguw +whatsapp.token=ajskjfaiebguw # ${WHATSAPP_TOKEN} whatsapp.api.version=v20.0 \ No newline at end of file From 77a0623f6baf6230f48c493a1e7af9603f7ae9db Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Wed, 14 May 2025 00:38:20 -0300 Subject: [PATCH 13/60] =?UTF-8?q?Emanuel=20me=20ajudou=20na=20documenta?= =?UTF-8?q?=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documentamos um pouco melhor o enum de UserStatus Co-Authored-By: Emmanuel N. C. Brito --- src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt index 3b8544b..635bcf1 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt @@ -6,7 +6,7 @@ package ufrpe.sbpc.botpcd.entity (INDISPONIVEL) ele foi ao banheiro / esta ocupado. Nao vai receber chamado ate que altere seu status manualmente para available */ enum class UserStatus { - AVAILABLE, - BUSY, - UNAVAILABLE + AVAILABLE("Disponível"), + BUSY("Ocupado"), + UNAVAILABLE("Indisponível") } \ No newline at end of file From 2c6c88c2ed61fce5756525b665b15e740d0b3f43 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Wed, 14 May 2025 00:39:09 -0300 Subject: [PATCH 14/60] =?UTF-8?q?Adicionei=20a=20atualiza=C3=A7=C3=A3o=20d?= =?UTF-8?q?e=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fiz com que ao iniciar ou encerrar um atendimento, o status do membro da comissão também possa ser mudado --- src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendance.kt | 1 + src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendance.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendance.kt index 056c123..d49d53d 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendance.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendance.kt @@ -30,6 +30,7 @@ class Attendance( @JoinColumn(name = "monitor_id") @NotNull(message = "The monitor is required") var monitor: Monitor, + var committeemember: CommitteeMember, // Campos opcionais que serão atualizados durante o ciclo de vida do atendimento var acceptDateTime: LocalDateTime? = null, diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt index cd66cef..75d4d83 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt @@ -18,12 +18,14 @@ class AttendanceService( @Transactional fun beginAttendance(attendance: Attendance) { userStatusService.setMonitorStatus(attendance.monitor, UserStatus.BUSY) + userStatusService.setCommitteeMemberStatus(attendance.committeemember, UserStatus.BUSY) attendanceRepository.save(attendance) } @Transactional fun endAttendance(attendance: Attendance) { userStatusService.setMonitorStatus(attendance.monitor, UserStatus.AVAILABLE) + userStatusService.setCommitteeMemberStatus(attendance.committeemember, UserStatus.AVAILABLE) attendance.endDateTime = java.time.LocalDateTime.now() attendanceRepository.save(attendance) } From d144ffb8aa96804bc7b66cb9f6bb7ff57c5165e8 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Wed, 14 May 2025 00:52:18 -0300 Subject: [PATCH 15/60] =?UTF-8?q?Implementa=C3=A7=C3=A3o=20da=20l=C3=B3gic?= =?UTF-8?q?a=20inicial=20de=20status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit O monitor quando iniciar o atendimento ele, vai falar o bot pcd e isso vai mostrar quais as opções ele tem (disponível, indisponível e ocupado) O Monitor vai digitar qual opção ele vai querer vai ter a opção de números ou botão - Caso 1 (ele está indisponível): Ficar Disponível 1 - Caso 2 (ele está ocupado): Encerrar atendimento 1 (vai internamente mudar o status dele pra disponível) Ficar indisponível 2 (ele para de ser chamado para atendimentos) - Caso 3 (ele está disponível): Ficar indisponível 1 Co-Authored-By: Emmanuel N. C. Brito --- .../sbpc/botpcd/service/AttendanceService.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt index 75d4d83..7679f7e 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt @@ -22,6 +22,22 @@ class AttendanceService( attendanceRepository.save(attendance) } + fun botCommand(attendance: Attendance) { + if (attendance.monitor.status == UserStatus.UNAVAILABLE) { + /*Ficar Disponível 1*/ + } + if (attendance.committeemember.status == UserStatus.BUSY) { + /* + Encerrar atendimento 1 (vai internamente mudar o status dele pra disponível) + + Ficar indisponível 2 (ele para de ser chamado para atendimentos) + */ + } + if (attendance.monitor.status == UserStatus.AVAILABLE) { + /*Ficar indisponível 1*/ + } + } + @Transactional fun endAttendance(attendance: Attendance) { userStatusService.setMonitorStatus(attendance.monitor, UserStatus.AVAILABLE) From 930c6bead2bdb65118bd4eaa8635718f4c29dc61 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:30:22 -0300 Subject: [PATCH 16/60] Criamos um arquivo para o service do whatsapp --- src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt new file mode 100644 index 0000000..49d7a5e --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -0,0 +1,4 @@ +package ufrpe.sbpc.botpcd.service + +class WhatsappService { +} \ No newline at end of file From f7b41b50f0b6fb3b245ebfb97b77977365fe1411 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:37:56 -0300 Subject: [PATCH 17/60] feat: Add a check logic on the "FirstContactService.kt". Its checks if receive "botpcd" and who send message. --- src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index aef63fe..f235943 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -2,6 +2,7 @@ package ufrpe.sbpc.botpcd.service import com.whatsapp.api.domain.webhook.Change import org.springframework.stereotype.Service +import ufrpe.sbpc.botpcd.entity.UserStatus import ufrpe.sbpc.botpcd.repository.CommitteeMemberRepository import ufrpe.sbpc.botpcd.repository.MonitorRepository import ufrpe.sbpc.botpcd.repository.PWDRepository From 5b391bb6beaf18fdf98e02497669580742546b66 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:41:36 -0300 Subject: [PATCH 18/60] refact: The entity now gets the Attendant Class instead of the User Class. --- src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt index c16fc44..d836743 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt @@ -3,6 +3,7 @@ package ufrpe.sbpc.botpcd.entity import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated +import jakarta.validation.constraints.NotEmpty /** * From 07c32bf1535ea7d0a6c1bb96a6cdba345832d16f Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:42:22 -0300 Subject: [PATCH 19/60] Changes by victor-yghor refact: The entity now gets the Attendant Class instead of the User Class. --- src/main/kotlin/ufrpe/sbpc/botpcd/entity/Monitor.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Monitor.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Monitor.kt index 1cd0c55..d34f521 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Monitor.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Monitor.kt @@ -14,9 +14,8 @@ import jakarta.validation.constraints.NotEmpty class Monitor( name: String, phoneNumber: String, + status: UserStatus, @Enumerated(value = EnumType.STRING) @NotEmpty(message = "Monitor needs to have an assistance type") - var assistanceTypes: MonitorAssistanceType, - @Enumerated(EnumType.STRING) - var status: UserStatus = UserStatus.AVAILABLE -) : User(name = name, phoneNumber = phoneNumber) \ No newline at end of file + var assistanceTypes: MonitorAssistanceType +) : Attendant(name = name, phoneNumber = phoneNumber, status = status) \ No newline at end of file From 77b581674ac5658ebf900cbd294a0b610a9d201c Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:43:32 -0300 Subject: [PATCH 20/60] feat: The entity Attendant was created. --- .../ufrpe/sbpc/botpcd/entity/Attendant.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendant.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendant.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendant.kt new file mode 100644 index 0000000..d8802c2 --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendant.kt @@ -0,0 +1,18 @@ +package ufrpe.sbpc.botpcd.entity + +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.Table +import jakarta.validation.constraints.NotEmpty + +@Entity +@Table(name = "tb_attendant") +abstract class Attendant( + name: String, + phoneNumber: String, + @Enumerated(EnumType.STRING) + var status: UserStatus = UserStatus.AVAILABLE +) : User(name = name, phoneNumber = phoneNumber) { + +} \ No newline at end of file From c1d87181500af653bb0ff10cdf696b3d502f9f35 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:44:41 -0300 Subject: [PATCH 21/60] refact: Now the Attendance entity gets the Attendant Class instead of a monitor or a committee member --- .../kotlin/ufrpe/sbpc/botpcd/entity/Attendance.kt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendance.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendance.kt index d49d53d..5964668 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendance.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendance.kt @@ -27,10 +27,13 @@ class Attendance( var pwd: PWD, @ManyToOne - @JoinColumn(name = "monitor_id") - @NotNull(message = "The monitor is required") - var monitor: Monitor, - var committeemember: CommitteeMember, + @JoinColumn(name = "attendant_id") + @NotNull(message = "The attendant ID is required") + var attendant: Attendant, + + @Enumerated(EnumType.STRING) + @NotNull(message = "The attendant type is required") + var attendantType: Provider, // Campos opcionais que serão atualizados durante o ciclo de vida do atendimento var acceptDateTime: LocalDateTime? = null, @@ -42,4 +45,3 @@ class Attendance( var endDateTime: LocalDateTime? = null ) - From 452d37e3da1530cafc51851a3429860d453a5e7d Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:45:02 -0300 Subject: [PATCH 22/60] Changes by victor-yghor refact: The entity now gets the Attendant Class instead of the User Class. --- src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt index d836743..8efdec2 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/CommitteeMember.kt @@ -12,6 +12,5 @@ import jakarta.validation.constraints.NotEmpty class CommitteeMember( name: String, phoneNumber: String, - @Enumerated(EnumType.STRING) - var status: UserStatus = UserStatus.AVAILABLE -): User(name = name, phoneNumber = phoneNumber) \ No newline at end of file + status: UserStatus +): Attendant(name = name, phoneNumber = phoneNumber, status = status) \ No newline at end of file From 7f48451f5f9eef1ec89ba80f8d2c19e6cf8d2a4f Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:45:32 -0300 Subject: [PATCH 23/60] Changes by victor-yghor refact: Added parameters in the enum. --- src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt index 635bcf1..bafb739 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/UserStatus.kt @@ -5,7 +5,7 @@ package ufrpe.sbpc.botpcd.entity (OCUPADO) ele esta em atendimento, mas quando finalizar o codigo trocara para disponivel (INDISPONIVEL) ele foi ao banheiro / esta ocupado. Nao vai receber chamado ate que altere seu status manualmente para available */ -enum class UserStatus { +enum class UserStatus(val text: String) { AVAILABLE("Disponível"), BUSY("Ocupado"), UNAVAILABLE("Indisponível") From 5fb13e953dd96c2483968031fd498a29825b26ab Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:48:47 -0300 Subject: [PATCH 24/60] Update AttendanceService.kt refact: Modified the logic of the beginAttendance function to instant change the status of the Attendant to BUSY and implemented the getAttendant to get either a Monitor or a Committee Member. Co-Authored-By: victor-yghor <170154820+victor-yghor@users.noreply.github.com> --- .../sbpc/botpcd/service/AttendanceService.kt | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt index 7679f7e..1474a08 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt @@ -8,34 +8,27 @@ import ufrpe.sbpc.botpcd.repository.AttendanceRepository @Service class AttendanceService( private val attendanceRepository: AttendanceRepository, - private val userStatusService: UserStatusService + private val userStatusService: StatusAttendService ) { fun searchAttendances(): List { return attendanceRepository.findAll() } - @Transactional - fun beginAttendance(attendance: Attendance) { - userStatusService.setMonitorStatus(attendance.monitor, UserStatus.BUSY) - userStatusService.setCommitteeMemberStatus(attendance.committeemember, UserStatus.BUSY) - attendanceRepository.save(attendance) - } - - fun botCommand(attendance: Attendance) { - if (attendance.monitor.status == UserStatus.UNAVAILABLE) { - /*Ficar Disponível 1*/ + fun getAttendant(attendant: Attendant): Any { + return when (attendant) { + Attendant.MONITOR -> attendance.attendant as Monitor + Attendant.COMMITTEE_MEMBER -> attendance.provider as CommitteeMember } - if (attendance.committeemember.status == UserStatus.BUSY) { - /* - Encerrar atendimento 1 (vai internamente mudar o status dele pra disponível) + } - Ficar indisponível 2 (ele para de ser chamado para atendimentos) - */ - } - if (attendance.monitor.status == UserStatus.AVAILABLE) { - /*Ficar indisponível 1*/ + @Transactional + fun beginAttendance(attendance: Attendance) { + when (attendance.attendantType) { + Attendant.MONITOR -> userStatusService.setMonitorStatus(attendance.attendant as Monitor, UserStatus.BUSY) + Attendant.COMMITTEE_MEMBER -> userStatusService.setCommitteeMemberStatus(attendance.attendant as CommitteeMember, UserStatus.BUSY) } + attendanceRepository.save(attendance) } @Transactional From b79369431a6ed8a8f7cca81a7401b24d9261f6ae Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:49:07 -0300 Subject: [PATCH 25/60] Delete UserStatusService.kt Co-Authored-By: victor-yghor <170154820+victor-yghor@users.noreply.github.com> --- .../sbpc/botpcd/service/UserStatusService.kt | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/service/UserStatusService.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/UserStatusService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/UserStatusService.kt deleted file mode 100644 index a62c128..0000000 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/UserStatusService.kt +++ /dev/null @@ -1,40 +0,0 @@ -package ufrpe.sbpc.botpcd.service - -import org.springframework.stereotype.Service -import ufrpe.sbpc.botpcd.entity.* -import ufrpe.sbpc.botpcd.repository.MonitorRepository -import ufrpe.sbpc.botpcd.repository.CommitteeMemberRepository -import org.springframework.transaction.annotation.Transactional - -@Service -class UserStatusService( - private val monitorRepository: MonitorRepository, - private val committeeMemberRepository: CommitteeMemberRepository -) { - - @Transactional - fun setMonitorStatus(monitor: Monitor, status: UserStatus) { - monitor.status = status - monitorRepository.save(monitor) - } - - @Transactional - fun setCommitteeMemberStatus(member: CommitteeMember, status: UserStatus) { - member.status = status - committeeMemberRepository.save(member) - } - - fun findAvailableMonitors(): List { - return monitorRepository.findByStatus(UserStatus.AVAILABLE) - } - - fun findAvailableCommitteeMembers(): List { - return committeeMemberRepository.findByStatus(UserStatus.AVAILABLE) - } - - // Método para filtrar por tipo de assistência também - fun findAvailableMonitorsByType(assistanceType: MonitorAssistanceType): List { - val availableMonitors = findAvailableMonitors() - return availableMonitors.filter { it.assistanceTypes == assistanceType } - } -} \ No newline at end of file From de3c2654364172df96b60cf8206ab7e61e5a2010 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:49:45 -0300 Subject: [PATCH 26/60] Update name refact: Changed the name of the service. Co-Authored-By: victor-yghor <170154820+victor-yghor@users.noreply.github.com> --- .../botpcd/service/AttendantStatusService.kt | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantStatusService.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantStatusService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantStatusService.kt new file mode 100644 index 0000000..432616c --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantStatusService.kt @@ -0,0 +1,65 @@ +package ufrpe.sbpc.botpcd.service + +import org.springframework.stereotype.Service +import ufrpe.sbpc.botpcd.entity.* +import ufrpe.sbpc.botpcd.repository.MonitorRepository +import ufrpe.sbpc.botpcd.repository.CommitteeMemberRepository +import org.springframework.transaction.annotation.Transactional + +@Service +class AttendantStatusService( + private val monitorRepository: MonitorRepository, + private val committeeMemberRepository: CommitteeMemberRepository +) { + + @Transactional + fun setMonitorStatus(monitor: Monitor, status: UserStatus) { + monitor.status = status + monitorRepository.save(monitor) + } + + @Transactional + fun setCommitteeMemberStatus(member: CommitteeMember, status: UserStatus) { + member.status = status + committeeMemberRepository.save(member) + } + + fun sendStatusChanger(attendant: Attendant) { + if (attendant.status == UserStatus.UNAVAILABLE) { + /*1 - Ficar Disponível*/ + /* + "Olá, você está Indisponível no momento. Deseja ficar Disponível para receber atendimentos?" + */ + } + if (attendant.status == UserStatus.BUSY) { + /* + 1 - Encerrar Atendimento (vai internamente mudar o status dele pra disponível) + + 2 - Ficar Indisponível (ele para de ser chamado para atendimentos) + */ + /* + "Olá, você está em atendimento. Deseja encerrá-lo e continuar Disnpoível ou deseja ficar Indisponível?" + */ + } + if (attendant.status == UserStatus.AVAILABLE) { + /*1 - Ficar Indisponível*/ + /* + "Olá, você está Disponível no momento. Deseja ficar Indisponível para não receber atendimentos?" + */ + } + } + + fun findAvailableMonitors(): List { + return monitorRepository.findByStatus(UserStatus.AVAILABLE) + } + + fun findAvailableCommitteeMembers(): List { + return committeeMemberRepository.findByStatus(UserStatus.AVAILABLE) + } + + // Método para filtrar por tipo de assistência também + fun findAvailableMonitorsByType(assistanceType: MonitorAssistanceType): List { + val availableMonitors = findAvailableMonitors() + return availableMonitors.filter { it.assistanceTypes == assistanceType } + } +} \ No newline at end of file From aaf50bf54b40f42e37aaad1dc7bf8e22de28bc20 Mon Sep 17 00:00:00 2001 From: Ronaldo Ribeiro Date: Fri, 16 May 2025 21:50:02 -0300 Subject: [PATCH 27/60] Update FirstContactService.kt Co-Authored-By: victor-yghor <170154820+victor-yghor@users.noreply.github.com> --- .../ufrpe/sbpc/botpcd/service/FirstContactService.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index f235943..21bab77 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -12,19 +12,29 @@ class FirstContactService( private val pwdRepository: PWDRepository, private val monitorRepository: MonitorRepository, private val committeeMemberRepository: CommitteeMemberRepository, - private val registerService: RegisterService + private val registerService: RegisterService, + private val attendanceStatusService: AttendantStatusService ) { fun redirectFluxByUserType(phoneNumber: String, change: Change) { when { + change.value.messages[0].text.body == "BotPCD" && (monitorRepository.findByPhoneNumber(phoneNumber) != null || (committeeMemberRepository.findByPhoneNumber( + phoneNumber + ) != null)) -> { + val attendant = monitorRepository.findByPhoneNumber(phoneNumber) ?: committeeMemberRepository.findByPhoneNumber( + phoneNumber)!! + attendanceStatusService.sendStatusChanger(attendant) + } pwdRepository.findByPhoneNumber(phoneNumber) != null -> { } monitorRepository.findByPhoneNumber(phoneNumber) != null -> { } + committeeMemberRepository.findByPhoneNumber(phoneNumber) != null -> { } + else -> { // Usuario não cadastrado registerService.registerPWD(phoneNumber, change) From 344294dea7eee5fc7a5a3e41eec8344c00a6b80d Mon Sep 17 00:00:00 2001 From: "Emmanuel N. C. Brito" Date: Mon, 19 May 2025 12:08:39 -0300 Subject: [PATCH 28/60] [feat] botpcd >> Add support to send mensage with whatsappService --- .../sbpc/botpcd/service/RegisterService.kt | 2 +- .../sbpc/botpcd/service/WhatsappService.kt | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterService.kt index 237f197..b1fde4b 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterService.kt @@ -5,7 +5,7 @@ import ufrpe.sbpc.botpcd.entity.Disability import org.springframework.stereotype.Service import com.whatsapp.api.domain.webhook.Change import ufrpe.sbpc.botpcd.repository.PWDRepository -import com.whatsapp.api.impl.WhatsappBusinessCloudApi +import ufrpe.sbpc.botpcd.service.WhatsappService @Service class RegisterService( diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt index 49d7a5e..44a9cef 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -1,4 +1,22 @@ package ufrpe.sbpc.botpcd.service +import com.whatsapp.api.impl.WhatsappBusinessCloudApi + class WhatsappService { -} \ No newline at end of file + fun sendMensage(phoneNumber: String, msg: String) { + val factory = WhatsappApiFactory.newInstance(TestUtils.TOKEN) + val whatsappBusinessCloudApi = factory.newBusinessCloudApi() + + val message = MessageBuilder.builder() + .setTo(phoneNumber) + .buildTextMessage(TextMessage().setBody(msg)) + .setPreviewUrl(false) + + whatsappBusinessCloudApi.sendMessage(PHONE_NUMBER_ID, message) + } + + fun sendButtons(phoneNumber: String, btn: Array) { + + } +} + From 4b46f45780ee5698beb9dbc0d804bd3ba2c4aeab Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 20 May 2025 11:44:04 -0300 Subject: [PATCH 29/60] put the cucumber testes to change status --- .../sbpc/botpcd/BotPcdApplicationTests.kt | 22 ++++++------ .../ufrpe/sbpc/botpcd/RunCucumberTest.kt | 28 +++++++-------- .../ufrpe/sbpc/botpcd/StepDefinitions.kt | 10 +++--- .../ufrpe/sbpc/botpcd/chageStatus.feature | 36 +++++++++++++++++++ 4 files changed, 66 insertions(+), 30 deletions(-) create mode 100644 src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/BotPcdApplicationTests.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/BotPcdApplicationTests.kt index 818e733..7e60b36 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/BotPcdApplicationTests.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/BotPcdApplicationTests.kt @@ -1,13 +1,13 @@ package ufrpe.sbpc.botpcd -//import org.junit.jupiter.api.Test -//import org.springframework.boot.test.context.SpringBootTest -// -//@SpringBootTest -//class BotPcdApplicationTests { -// -// @Test -// fun contextLoads() { -// } -// -//} +import org.junit.jupiter.api.Test +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest +class BotPcdApplicationTests { + + @Test + fun contextLoads() { + } + +} diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/RunCucumberTest.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/RunCucumberTest.kt index 417c302..70b9812 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/RunCucumberTest.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/RunCucumberTest.kt @@ -1,16 +1,16 @@ package ufrpe.sbpc.botpcd -//import io.cucumber.core.options.Constants.GLUE_PROPERTY_NAME -//import io.cucumber.core.options.Constants.PLUGIN_PROPERTY_NAME -//import org.junit.platform.suite.api.ConfigurationParameter -//import org.junit.platform.suite.api.IncludeEngines -//import org.junit.platform.suite.api.SelectPackages -//import org.junit.platform.suite.api.Suite -// -//@Suite -//@IncludeEngines("cucumber") -//@SelectPackages("ufrpe.sbpc.botpcd") -//@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") -//@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "ufrpe.sbpc.botpcd") -//class RunCucumberTest { -//} \ No newline at end of file +import io.cucumber.core.options.Constants.GLUE_PROPERTY_NAME +import io.cucumber.core.options.Constants.PLUGIN_PROPERTY_NAME +import org.junit.platform.suite.api.ConfigurationParameter +import org.junit.platform.suite.api.IncludeEngines +import org.junit.platform.suite.api.SelectPackages +import org.junit.platform.suite.api.Suite + +@Suite +@IncludeEngines("cucumber") +@SelectPackages("ufrpe.sbpc.botpcd") +@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") +@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "ufrpe.sbpc.botpcd") +class RunCucumberTest { +} \ No newline at end of file diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt index 9caf291..6acb986 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt @@ -1,7 +1,7 @@ package ufrpe.sbpc.botpcd -//import io.cucumber.java.en.Given -// -//class StepDefinitions { -// -//} +import io.cucumber.java.en.Given + +class StepDefinitions { + +} diff --git a/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature b/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature new file mode 100644 index 0000000..7e0320d --- /dev/null +++ b/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature @@ -0,0 +1,36 @@ +Feature: Mudar status do Atendente(Monitor ou membro da comissão) +Como um Atendente(Monitor ou membro da comissão), Eu quero mudar meu status através de uma interação com bot do whatsapp, Para que minha disponibilidade seja sempre atualizada. Os possíveis status são: +*Disponível quando ele iniciar o dia e também quando finalizar um atendimento. +*Ocupado durante um atendimento +*Indisponível quando encerrar o dia ou quando for ao banheiro, por exemplo. + + +Esquema do Cenário: Solicitar mudança de status +Dado que um está com status +Quando inicia a interação para mudar o status +Então o sistema deve apresentar uma + +Exemplos: +| tipo_de_atendente | status_atual |mensagem_de_troca_de_status| +| monitor | disponível | Olá, você está Disponível no momento. Deseja ficar Indisponível para não receber atendimentos? | +| monitor | indisponível | Olá, você está Indisponível no momento. Deseja ficar Disponível para receber atendimentos? | +| monitor | ocupado | Olá, você está em atendimento. Deseja encerrá-lo e continuar Disnpoível ou deseja ficar Indisponível?| +| membro da comissão| disponível | Olá, você está Disponível no momento. Deseja ficar Indisponível para não receber atendimentos? | +| membro da comissão| indisponível | Olá, você está Indisponível no momento. Deseja ficar Disponível para receber atendimentos? | +| membro da commissão | ocupado | Olá, você está em atendimento. Deseja encerrá-lo e continuar Disnpoível ou deseja ficar Indisponível?| + +Esquema do Cenário: Confirmar mudança de status +Dado que o está no recebeu uma solicitação para mudar para o status +Quando o confirma a mudança +Então o status do deve ser atualizado para E o sistema deve notificar sobre a mudança bem-sucedida + +Exemplos: +| tipo_de_atendente | antigo_status | novo_status | +| monitor | disponível | indisponível | +| monitor | ocupado | indisponível | +| monitor | ocupado | disponível | +| monitor | indisponível | disponível | +| membro da comissão | disponível | indisponível | +| membro da comissão | ocupado | indisponível | +| membro da comissão | ocupado | disponível | +| membro da comissão | indisponível | disponível | From 00e421036435be5462b5469c09bba0859ee8774b Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 20 May 2025 20:00:38 -0300 Subject: [PATCH 30/60] Fix problem with the indentation of changeStatus.feature --- .../ufrpe/sbpc/botpcd/chageStatus.feature | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature b/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature index 7e0320d..2516e80 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature @@ -1,36 +1,36 @@ -Feature: Mudar status do Atendente(Monitor ou membro da comissão) -Como um Atendente(Monitor ou membro da comissão), Eu quero mudar meu status através de uma interação com bot do whatsapp, Para que minha disponibilidade seja sempre atualizada. Os possíveis status são: -*Disponível quando ele iniciar o dia e também quando finalizar um atendimento. -*Ocupado durante um atendimento -*Indisponível quando encerrar o dia ou quando for ao banheiro, por exemplo. +# language: pt +Funcionalidade: Mudar status do Atendente(Monitor ou membro da comissão) + Como um Atendente(Monitor ou membro da comissão), Eu quero mudar meu status através de uma interação com bot do whatsapp, Para que minha disponibilidade seja sempre atualizada. Os possíveis status são: + *Disponível quando ele iniciar o dia e também quando finalizar um atendimento. + *Ocupado durante um atendimento + *Indisponível quando encerrar o dia ou quando for ao banheiro, por exemplo. + Esquema do Cenário: Solicitar mudança de status + Dado que um está com status + Quando inicia a interação para mudar o status + Então o sistema deve apresentar uma -Esquema do Cenário: Solicitar mudança de status -Dado que um está com status -Quando inicia a interação para mudar o status -Então o sistema deve apresentar uma + Exemplos: + | tipo_de_atendente | status_atual | mensagem_de_troca_de_status | + | monitor | disponível | Olá, você está Disponível no momento. Deseja ficar Indisponível para não receber atendimentos? | + | monitor | indisponível | Olá, você está Indisponível no momento. Deseja ficar Disponível para receber atendimentos? | + | monitor | ocupado | Olá, você está em atendimento. Deseja encerrá-lo e continuar Disnpoível ou deseja ficar Indisponível? | + | membro da comissão | disponível | Olá, você está Disponível no momento. Deseja ficar Indisponível para não receber atendimentos? | + | membro da comissão | indisponível | Olá, você está Indisponível no momento. Deseja ficar Disponível para receber atendimentos? | + | membro da commissão | ocupado | Olá, você está em atendimento. Deseja encerrá-lo e continuar Disnpoível ou deseja ficar Indisponível? | -Exemplos: -| tipo_de_atendente | status_atual |mensagem_de_troca_de_status| -| monitor | disponível | Olá, você está Disponível no momento. Deseja ficar Indisponível para não receber atendimentos? | -| monitor | indisponível | Olá, você está Indisponível no momento. Deseja ficar Disponível para receber atendimentos? | -| monitor | ocupado | Olá, você está em atendimento. Deseja encerrá-lo e continuar Disnpoível ou deseja ficar Indisponível?| -| membro da comissão| disponível | Olá, você está Disponível no momento. Deseja ficar Indisponível para não receber atendimentos? | -| membro da comissão| indisponível | Olá, você está Indisponível no momento. Deseja ficar Disponível para receber atendimentos? | -| membro da commissão | ocupado | Olá, você está em atendimento. Deseja encerrá-lo e continuar Disnpoível ou deseja ficar Indisponível?| + Esquema do Cenário: Confirmar mudança de status + Dado que o está no recebeu uma solicitação para mudar para o status + Quando o confirma a mudança + Então o status do deve ser atualizado para E o sistema deve notificar sobre a mudança bem-sucedida -Esquema do Cenário: Confirmar mudança de status -Dado que o está no recebeu uma solicitação para mudar para o status -Quando o confirma a mudança -Então o status do deve ser atualizado para E o sistema deve notificar sobre a mudança bem-sucedida - -Exemplos: -| tipo_de_atendente | antigo_status | novo_status | -| monitor | disponível | indisponível | -| monitor | ocupado | indisponível | -| monitor | ocupado | disponível | -| monitor | indisponível | disponível | -| membro da comissão | disponível | indisponível | -| membro da comissão | ocupado | indisponível | -| membro da comissão | ocupado | disponível | -| membro da comissão | indisponível | disponível | + Exemplos: + | tipo_de_atendente | antigo_status | novo_status | + | monitor | disponível | indisponível | + | monitor | ocupado | indisponível | + | monitor | ocupado | disponível | + | monitor | indisponível | disponível | + | membro da comissão | disponível | indisponível | + | membro da comissão | ocupado | indisponível | + | membro da comissão | ocupado | disponível | + | membro da comissão | indisponível | disponível | From bca45d717ff4c09a3527d13a7b19aac3243c61f2 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Fri, 23 May 2025 20:13:35 -0300 Subject: [PATCH 31/60] =?UTF-8?q?change=20the=20changeStatus=20cen=C3=A1ri?= =?UTF-8?q?o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ufrpe/sbpc/botpcd/chageStatus.feature | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature b/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature index 2516e80..2e74c7a 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature @@ -5,24 +5,24 @@ Funcionalidade: Mudar status do Atendente(Monitor ou membro da comissão) *Ocupado durante um atendimento *Indisponível quando encerrar o dia ou quando for ao banheiro, por exemplo. + Esquema do Cenário: Solicitar mudança de status Dado que um está com status Quando inicia a interação para mudar o status - Então o sistema deve apresentar uma - + Então o bot envia a mensagem “Olá, ! No momento, seu status está como . Para mudar para algum desses status , basta clicar no botão correspondente abaixo.” E o bot envia a lista de botões dos possíveis novos status. Exemplos: - | tipo_de_atendente | status_atual | mensagem_de_troca_de_status | - | monitor | disponível | Olá, você está Disponível no momento. Deseja ficar Indisponível para não receber atendimentos? | - | monitor | indisponível | Olá, você está Indisponível no momento. Deseja ficar Disponível para receber atendimentos? | - | monitor | ocupado | Olá, você está em atendimento. Deseja encerrá-lo e continuar Disnpoível ou deseja ficar Indisponível? | - | membro da comissão | disponível | Olá, você está Disponível no momento. Deseja ficar Indisponível para não receber atendimentos? | - | membro da comissão | indisponível | Olá, você está Indisponível no momento. Deseja ficar Disponível para receber atendimentos? | - | membro da commissão | ocupado | Olá, você está em atendimento. Deseja encerrá-lo e continuar Disnpoível ou deseja ficar Indisponível? | + | tipo_de_atendente | status_atual | novo_status | + | monitor | disponível | indisponível | + | monitor | indisponível | disponível | + | monitor | ocupado | disponível,indisponível | + | membro da comissão | disponível | indisponível | + | membro da comissão | indisponível | indisponível | + | membro da comissão | ocupado | disponível,indisponível | Esquema do Cenário: Confirmar mudança de status Dado que o está no recebeu uma solicitação para mudar para o status Quando o confirma a mudança - Então o status do deve ser atualizado para E o sistema deve notificar sobre a mudança bem-sucedida + Então o status do deve ser atualizado para E o bot deve enviar a mensagem “Mudança realizada com sucesso agora seu novo status é ”. Exemplos: | tipo_de_atendente | antigo_status | novo_status | From 49bcd9684c8fcbcae0a3d6e332842c9789ef03db Mon Sep 17 00:00:00 2001 From: "Emmanuel N. C. Brito" Date: Mon, 26 May 2025 22:36:09 -0300 Subject: [PATCH 32/60] work: Add part of button creation in whatsapp-service --- .../sbpc/botpcd/service/WhatsappService.kt | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt index 44a9cef..b8eb67c 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -1,22 +1,47 @@ +//====================================================================== package ufrpe.sbpc.botpcd.service import com.whatsapp.api.impl.WhatsappBusinessCloudApi +//====================================================================== +object WhatsappService { + private const val TOKEN = "seu_token_aqui" + private const val PhoneNumber = "seu_phone_number" -class WhatsappService { - fun sendMensage(phoneNumber: String, msg: String) { - val factory = WhatsappApiFactory.newInstance(TestUtils.TOKEN) - val whatsappBusinessCloudApi = factory.newBusinessCloudApi() + private val factory: WhatsappApiFactory = WhatsappApiFactory.newInstance(TOKEN) + private val cloudApi: WhatsappBusinessCloudApi = factory.newBusinessCloudApi() +//====================================================================== + fun sendMensage(destinyNumberID: String, msg: String) { + val message = MessageBuilder.builder() + .setTo(phoneNumber) + .buildTextMessage(TextMessage().setBody(msg)) - val message = MessageBuilder.builder() - .setTo(phoneNumber) - .buildTextMessage(TextMessage().setBody(msg)) - .setPreviewUrl(false) + cloudApi.sendMessage(destinyNumberID, message) + } +//====================================================================== + fun sendButtons(destinyNumberID: String, btn: Array) { + val action = Action() + btn.forEachIndexed { index, title -> + val button = Button() + .setType(ButtonType.REPLY) + .setReply( + Reply() + .setId("BUTTON_ID_$index") + .setTitle(title) + ) + action.addButton(button) + } - whatsappBusinessCloudApi.sendMessage(PHONE_NUMBER_ID, message) - } + val interactiveMessage = InteractiveMessage.build() + .setAction(action) + .setType(InteractiveMessageType.BUTTON) + .setBody(Body().setText("Escolha uma opção:")) + + val message = MessageBuilder.builder() + .setTo(phoneNumber) + .buildInteractiveMessage(interactiveMessage) - fun sendButtons(phoneNumber: String, btn: Array) { - + cloudApi.sendMessage(PHONE_NUMBER_ID, message) } } +//====================================================================== From 6cdd26c91921483f0eb0dc356f787fe440f9dff8 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Mon, 26 May 2025 22:40:38 -0300 Subject: [PATCH 33/60] WIP start the register logic --- .../sbpc/botpcd/service/FirstContactService.kt | 4 ++-- .../sbpc/botpcd/service/RegisterPWDService.kt | 14 +++++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index 2503065..900ec84 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -11,7 +11,7 @@ class FirstContactService( private val pwdRepository: PWDRepository, private val monitorRepository: MonitorRepository, private val committeeMemberRepository: CommitteeMemberRepository, - private val registerService: RegisterPWDService + private val registerPWDService: RegisterPWDService ) { fun redirectFluxByUserType(phoneNumber: String, change: Change) { // to get a message from the change change.value.messages[0].text.body @@ -30,7 +30,7 @@ class FirstContactService( } else -> { // Usuario não cadastrado -// registerService.registerPWD(phoneNumber, change) + registerPWDService.sendDisabilities(phoneNumber) } } } diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt index 8ecf374..f552096 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt @@ -3,13 +3,25 @@ package ufrpe.sbpc.botpcd.service import org.springframework.stereotype.Service import ufrpe.sbpc.botpcd.repository.PWDRepository import com.whatsapp.api.impl.WhatsappBusinessCloudApi +import ufrpe.sbpc.botpcd.entity.Disability +import ufrpe.sbpc.botpcd.entity.PWD @Service class RegisterPWDService( private val pwdRepository: PWDRepository, private val whatsappBusinessCloudApi: WhatsappBusinessCloudApi, ) { - fun registerDisability() { + fun registerDisability(pwdPhoneNumber: String, disability: Disability) { + + } + fun registerName(pwd: PWD, name: String) { + + } + fun whatIsYourDisability(pwdPhoneNumber: String) { + + } + + fun whatsIsYourName(pwdPhoneNumber: String) { } } From 930581e409a1a248184a37f7218af720932fb5bb Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Mon, 26 May 2025 22:55:41 -0300 Subject: [PATCH 34/60] Fix the WhatsappService.kt --- .../sbpc/botpcd/service/WhatsappService.kt | 78 ++++++++++--------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt index b8eb67c..53ebc1d 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -1,47 +1,51 @@ -//====================================================================== package ufrpe.sbpc.botpcd.service +import com.whatsapp.api.domain.messages.Action +import com.whatsapp.api.domain.messages.Body +import com.whatsapp.api.domain.messages.Button +import com.whatsapp.api.domain.messages.InteractiveMessage +import com.whatsapp.api.domain.messages.Message +import com.whatsapp.api.domain.messages.Reply +import com.whatsapp.api.domain.messages.TextMessage +import com.whatsapp.api.domain.messages.type.ButtonType +import com.whatsapp.api.domain.messages.type.InteractiveMessageType import com.whatsapp.api.impl.WhatsappBusinessCloudApi -//====================================================================== -object WhatsappService { - private const val TOKEN = "seu_token_aqui" - private const val PhoneNumber = "seu_phone_number" +import org.springframework.stereotype.Service - private val factory: WhatsappApiFactory = WhatsappApiFactory.newInstance(TOKEN) - private val cloudApi: WhatsappBusinessCloudApi = factory.newBusinessCloudApi() -//====================================================================== - fun sendMensage(destinyNumberID: String, msg: String) { - val message = MessageBuilder.builder() - .setTo(phoneNumber) - .buildTextMessage(TextMessage().setBody(msg)) +@Service +class WhatsappService( + private val cloudApi: WhatsappBusinessCloudApi +) { + + fun sendMessage(destinyNumberID: String, msg: String) { + val message = Message.MessageBuilder.builder() + .setTo(destinyNumberID) + .buildTextMessage(TextMessage().setBody(msg)) cloudApi.sendMessage(destinyNumberID, message) } -//====================================================================== - fun sendButtons(destinyNumberID: String, btn: Array) { - val action = Action() - btn.forEachIndexed { index, title -> - val button = Button() - .setType(ButtonType.REPLY) - .setReply( - Reply() - .setId("BUTTON_ID_$index") - .setTitle(title) - ) - action.addButton(button) - } - val interactiveMessage = InteractiveMessage.build() - .setAction(action) - .setType(InteractiveMessageType.BUTTON) - .setBody(Body().setText("Escolha uma opção:")) - - val message = MessageBuilder.builder() - .setTo(phoneNumber) - .buildInteractiveMessage(interactiveMessage) - - cloudApi.sendMessage(PHONE_NUMBER_ID, message) - } + fun sendButtons(destinyNumberID: String, buttons: Array, titleMessage: String) { + val action = Action() + buttons.forEachIndexed { index, title -> + val button = Button() + .setType(ButtonType.REPLY) + .setReply( + Reply() + .setId("BUTTON_ID_$index") + .setTitle(title) + ) + action.addButton(button) + } + val interactiveMessage = InteractiveMessage.build() + .setAction(action) + .setType(InteractiveMessageType.BUTTON) + .setBody(Body().setText(titleMessage)) + val message = Message.MessageBuilder.builder() + .setTo(destinyNumberID) + .buildInteractiveMessage(interactiveMessage) + cloudApi.sendMessage(destinyNumberID, message) + } } -//====================================================================== + From 6c10b752d4ebacde980002059aae1ce6be640025 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Mon, 26 May 2025 23:44:48 -0300 Subject: [PATCH 35/60] Send a list of disabilities to the user --- .../ufrpe/sbpc/botpcd/entity/Disability.kt | 3 + .../sbpc/botpcd/service/AttendanceService.kt | 82 +++++++++---------- .../botpcd/service/FirstContactService.kt | 5 +- .../sbpc/botpcd/service/RegisterPWDService.kt | 5 +- .../sbpc/botpcd/service/WhatsappService.kt | 2 +- 5 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt index 091ad24..53fb979 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt @@ -23,5 +23,8 @@ enum class Disability(val textOption: String) { fun textList(): Array { return Disability.entries.map { it.textOption }.toTypedArray() } + fun disabilitiesOptions(): List { + return listOf(*textList(), "Não preciso de suporte") + } } } diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt index 1474a08..12ef51c 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt @@ -1,42 +1,42 @@ package ufrpe.sbpc.botpcd.service - -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import ufrpe.sbpc.botpcd.entity.* -import ufrpe.sbpc.botpcd.repository.AttendanceRepository - -@Service -class AttendanceService( - private val attendanceRepository: AttendanceRepository, - private val userStatusService: StatusAttendService -) { - - fun searchAttendances(): List { - return attendanceRepository.findAll() - } - - fun getAttendant(attendant: Attendant): Any { - return when (attendant) { - Attendant.MONITOR -> attendance.attendant as Monitor - Attendant.COMMITTEE_MEMBER -> attendance.provider as CommitteeMember - } - } - - @Transactional - fun beginAttendance(attendance: Attendance) { - when (attendance.attendantType) { - Attendant.MONITOR -> userStatusService.setMonitorStatus(attendance.attendant as Monitor, UserStatus.BUSY) - Attendant.COMMITTEE_MEMBER -> userStatusService.setCommitteeMemberStatus(attendance.attendant as CommitteeMember, UserStatus.BUSY) - } - attendanceRepository.save(attendance) - } - - @Transactional - fun endAttendance(attendance: Attendance) { - userStatusService.setMonitorStatus(attendance.monitor, UserStatus.AVAILABLE) - userStatusService.setCommitteeMemberStatus(attendance.committeemember, UserStatus.AVAILABLE) - attendance.endDateTime = java.time.LocalDateTime.now() - attendanceRepository.save(attendance) - } - -} \ No newline at end of file +// +//import org.springframework.stereotype.Service +//import org.springframework.transaction.annotation.Transactional +//import ufrpe.sbpc.botpcd.entity.* +//import ufrpe.sbpc.botpcd.repository.AttendanceRepository +// +//@Service +//class AttendanceService( +// private val attendanceRepository: AttendanceRepository, +// private val userStatusService: StatusAttendService +//) { +// +// fun searchAttendances(): List { +// return attendanceRepository.findAll() +// } +// +// fun getAttendant(attendant: Attendant): Any { +// return when (attendant) { +// Attendant.MONITOR -> attendance.attendant as Monitor +// Attendant.COMMITTEE_MEMBER -> attendance.provider as CommitteeMember +// } +// } +// +// @Transactional +// fun beginAttendance(attendance: Attendance) { +// when (attendance.attendantType) { +// Attendant.MONITOR -> userStatusService.setMonitorStatus(attendance.attendant as Monitor, UserStatus.BUSY) +// Attendant.COMMITTEE_MEMBER -> userStatusService.setCommitteeMemberStatus(attendance.attendant as CommitteeMember, UserStatus.BUSY) +// } +// attendanceRepository.save(attendance) +// } +// +// @Transactional +// fun endAttendance(attendance: Attendance) { +// userStatusService.setMonitorStatus(attendance.monitor, UserStatus.AVAILABLE) +// userStatusService.setCommitteeMemberStatus(attendance.committeemember, UserStatus.AVAILABLE) +// attendance.endDateTime = java.time.LocalDateTime.now() +// attendanceRepository.save(attendance) +// } +// +//} \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index 900ec84..c30c44f 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -11,7 +11,8 @@ class FirstContactService( private val pwdRepository: PWDRepository, private val monitorRepository: MonitorRepository, private val committeeMemberRepository: CommitteeMemberRepository, - private val registerPWDService: RegisterPWDService + private val registerPWDService: RegisterPWDService, + private val whatsappService: WhatsappService ) { fun redirectFluxByUserType(phoneNumber: String, change: Change) { // to get a message from the change change.value.messages[0].text.body @@ -30,7 +31,7 @@ class FirstContactService( } else -> { // Usuario não cadastrado - registerPWDService.sendDisabilities(phoneNumber) + registerPWDService.whatIsYourDisability(phoneNumber) } } } diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt index f552096..372d3da 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt @@ -3,13 +3,14 @@ package ufrpe.sbpc.botpcd.service import org.springframework.stereotype.Service import ufrpe.sbpc.botpcd.repository.PWDRepository import com.whatsapp.api.impl.WhatsappBusinessCloudApi +import org.springframework.aot.hint.TypeReference.listOf import ufrpe.sbpc.botpcd.entity.Disability import ufrpe.sbpc.botpcd.entity.PWD @Service class RegisterPWDService( private val pwdRepository: PWDRepository, - private val whatsappBusinessCloudApi: WhatsappBusinessCloudApi, + private val whatsappService: WhatsappService ) { fun registerDisability(pwdPhoneNumber: String, disability: Disability) { @@ -18,7 +19,7 @@ class RegisterPWDService( } fun whatIsYourDisability(pwdPhoneNumber: String) { - + whatsappService.sendButtons(pwdPhoneNumber, Disability.disabilitiesOptions(), "Olá, qual sua deficiência:") } fun whatsIsYourName(pwdPhoneNumber: String) { diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt index 53ebc1d..d0e4cb5 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -25,7 +25,7 @@ class WhatsappService( cloudApi.sendMessage(destinyNumberID, message) } - fun sendButtons(destinyNumberID: String, buttons: Array, titleMessage: String) { + fun sendButtons(destinyNumberID: String, buttons: List, titleMessage: String) { val action = Action() buttons.forEachIndexed { index, title -> val button = Button() From 4936f237800fb41e9dad22034b6f89e8bc68b3b4 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 27 May 2025 00:36:49 -0300 Subject: [PATCH 36/60] try to send message to the user --- .../kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt | 3 ++- .../kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt | 4 ++-- src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index c30c44f..d891bbe 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -16,6 +16,7 @@ class FirstContactService( ) { fun redirectFluxByUserType(phoneNumber: String, change: Change) { // to get a message from the change change.value.messages[0].text.body + val botNumber = change.value.metadata.phoneNumberId when { pwdRepository.findByPhoneNumber(phoneNumber) != null -> { val pwd = pwdRepository.findByPhoneNumber(phoneNumber)!! @@ -31,7 +32,7 @@ class FirstContactService( } else -> { // Usuario não cadastrado - registerPWDService.whatIsYourDisability(phoneNumber) + registerPWDService.whatIsYourDisability(botNumber, phoneNumber) } } } diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt index 372d3da..901f624 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt @@ -18,8 +18,8 @@ class RegisterPWDService( fun registerName(pwd: PWD, name: String) { } - fun whatIsYourDisability(pwdPhoneNumber: String) { - whatsappService.sendButtons(pwdPhoneNumber, Disability.disabilitiesOptions(), "Olá, qual sua deficiência:") + fun whatIsYourDisability(botNumber: String, pwdPhoneNumber: String) { + whatsappService.sendButtons(botNumber,pwdPhoneNumber, Disability.disabilitiesOptions(), "Olá, qual sua deficiência:") } fun whatsIsYourName(pwdPhoneNumber: String) { diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt index d0e4cb5..0dd006b 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -10,6 +10,7 @@ import com.whatsapp.api.domain.messages.TextMessage import com.whatsapp.api.domain.messages.type.ButtonType import com.whatsapp.api.domain.messages.type.InteractiveMessageType import com.whatsapp.api.impl.WhatsappBusinessCloudApi +import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Service @@ -18,14 +19,14 @@ class WhatsappService( private val cloudApi: WhatsappBusinessCloudApi ) { - fun sendMessage(destinyNumberID: String, msg: String) { + fun sendMessage(botNumber: String, destinyNumberID: String, msg: String) { val message = Message.MessageBuilder.builder() .setTo(destinyNumberID) .buildTextMessage(TextMessage().setBody(msg)) cloudApi.sendMessage(destinyNumberID, message) } - fun sendButtons(destinyNumberID: String, buttons: List, titleMessage: String) { + fun sendButtons(botNumber: String, destinyNumberID: String, buttons: List, titleMessage: String) { val action = Action() buttons.forEachIndexed { index, title -> val button = Button() From 2717032c135c09da3e8cfdae11c088000121b229 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 27 May 2025 09:43:41 -0300 Subject: [PATCH 37/60] Fix problem sending messages --- src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt index 0dd006b..6cfa78d 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -23,7 +23,7 @@ class WhatsappService( val message = Message.MessageBuilder.builder() .setTo(destinyNumberID) .buildTextMessage(TextMessage().setBody(msg)) - cloudApi.sendMessage(destinyNumberID, message) + cloudApi.sendMessage(botNumber, message) } fun sendButtons(botNumber: String, destinyNumberID: String, buttons: List, titleMessage: String) { @@ -45,7 +45,7 @@ class WhatsappService( val message = Message.MessageBuilder.builder() .setTo(destinyNumberID) .buildInteractiveMessage(interactiveMessage) - cloudApi.sendMessage(destinyNumberID, message) + cloudApi.sendMessage(botNumber, message) } } From 63528e391245502e68b513ab8afc236c132920bc Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 27 May 2025 10:21:42 -0300 Subject: [PATCH 38/60] Send Whatsapp message --- .../sbpc/botpcd/service/RegisterPWDService.kt | 2 +- .../sbpc/botpcd/service/WhatsappService.kt | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt index 901f624..ee9e976 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt @@ -19,7 +19,7 @@ class RegisterPWDService( } fun whatIsYourDisability(botNumber: String, pwdPhoneNumber: String) { - whatsappService.sendButtons(botNumber,pwdPhoneNumber, Disability.disabilitiesOptions(), "Olá, qual sua deficiência:") + whatsappService.sendButtons(botNumber,pwdPhoneNumber, Disability.disabilitiesOptions(), "Qual sua deficiência") } fun whatsIsYourName(pwdPhoneNumber: String) { diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt index 6cfa78d..f28d28e 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -2,15 +2,12 @@ package ufrpe.sbpc.botpcd.service import com.whatsapp.api.domain.messages.Action import com.whatsapp.api.domain.messages.Body -import com.whatsapp.api.domain.messages.Button import com.whatsapp.api.domain.messages.InteractiveMessage import com.whatsapp.api.domain.messages.Message -import com.whatsapp.api.domain.messages.Reply +import com.whatsapp.api.domain.messages.Section import com.whatsapp.api.domain.messages.TextMessage -import com.whatsapp.api.domain.messages.type.ButtonType import com.whatsapp.api.domain.messages.type.InteractiveMessageType import com.whatsapp.api.impl.WhatsappBusinessCloudApi -import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Service @@ -27,24 +24,27 @@ class WhatsappService( } fun sendButtons(botNumber: String, destinyNumberID: String, buttons: List, titleMessage: String) { - val action = Action() - buttons.forEachIndexed { index, title -> - val button = Button() - .setType(ButtonType.REPLY) - .setReply( - Reply() - .setId("BUTTON_ID_$index") - .setTitle(title) - ) - action.addButton(button) + val section = Section().setTitle("Selecione") + + buttons.forEachIndexed { index, buttonText -> + val row = com.whatsapp.api.domain.messages.Row() + .setId("row_$index") + .setTitle("Opção ${index + 1}") + .setDescription(buttonText) + section.addRow(row) } + val interactiveMessage = InteractiveMessage.build() - .setAction(action) - .setType(InteractiveMessageType.BUTTON) - .setBody(Body().setText(titleMessage)) + .setAction(Action().setButtonText(titleMessage).addSection(section)) + .setType(InteractiveMessageType.LIST) + .setHeader(com.whatsapp.api.domain.messages.Header().setType(com.whatsapp.api.domain.messages.type.HeaderType.TEXT).setText(titleMessage)) + .setBody(Body().setText("Selecione seu tipo de deficiẽncia:")) + .setFooter(com.whatsapp.api.domain.messages.Footer().setText("Rodapé")) + val message = Message.MessageBuilder.builder() .setTo(destinyNumberID) .buildInteractiveMessage(interactiveMessage) + cloudApi.sendMessage(botNumber, message) } } From 2a968dc1637944b7f4636c362506a4c9d6ff3988 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Fri, 30 May 2025 11:56:26 -0300 Subject: [PATCH 39/60] Put the registerPCD.feature --- .../ufrpe/sbpc/botpcd/chageStatus.feature | 36 ------------------- .../ufrpe/sbpc/botpcd/registerPCD.feature | 25 +++++++++++++ 2 files changed, 25 insertions(+), 36 deletions(-) delete mode 100644 src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature create mode 100644 src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature diff --git a/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature b/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature deleted file mode 100644 index 2e74c7a..0000000 --- a/src/test/resources/ufrpe/sbpc/botpcd/chageStatus.feature +++ /dev/null @@ -1,36 +0,0 @@ -# language: pt -Funcionalidade: Mudar status do Atendente(Monitor ou membro da comissão) - Como um Atendente(Monitor ou membro da comissão), Eu quero mudar meu status através de uma interação com bot do whatsapp, Para que minha disponibilidade seja sempre atualizada. Os possíveis status são: - *Disponível quando ele iniciar o dia e também quando finalizar um atendimento. - *Ocupado durante um atendimento - *Indisponível quando encerrar o dia ou quando for ao banheiro, por exemplo. - - - Esquema do Cenário: Solicitar mudança de status - Dado que um está com status - Quando inicia a interação para mudar o status - Então o bot envia a mensagem “Olá, ! No momento, seu status está como . Para mudar para algum desses status , basta clicar no botão correspondente abaixo.” E o bot envia a lista de botões dos possíveis novos status. - Exemplos: - | tipo_de_atendente | status_atual | novo_status | - | monitor | disponível | indisponível | - | monitor | indisponível | disponível | - | monitor | ocupado | disponível,indisponível | - | membro da comissão | disponível | indisponível | - | membro da comissão | indisponível | indisponível | - | membro da comissão | ocupado | disponível,indisponível | - - Esquema do Cenário: Confirmar mudança de status - Dado que o está no recebeu uma solicitação para mudar para o status - Quando o confirma a mudança - Então o status do deve ser atualizado para E o bot deve enviar a mensagem “Mudança realizada com sucesso agora seu novo status é ”. - - Exemplos: - | tipo_de_atendente | antigo_status | novo_status | - | monitor | disponível | indisponível | - | monitor | ocupado | indisponível | - | monitor | ocupado | disponível | - | monitor | indisponível | disponível | - | membro da comissão | disponível | indisponível | - | membro da comissão | ocupado | indisponível | - | membro da comissão | ocupado | disponível | - | membro da comissão | indisponível | disponível | diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature new file mode 100644 index 0000000..dcb5bc0 --- /dev/null +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -0,0 +1,25 @@ +# language: pt +Funcionalidade: Cadastro do PCD + Como um PCD, quero poder me cadastrar para acessar os serviços da SBPC com mais facilidade. + + Cenario: O Usuário não cadastrado manda qualquer mensagem + Dado Um Usuário não cadastrado + Quando Usuário mandar qualquer mensagem para o botpcd + Entao O bot envia a mensagem Olá, qual sua deficiência: E bot envia uma lista de botões com as seguintes deficiências: Deficiência visual, Deficiência auditiva/surdez, Surdocegueira, TEA/Neurodivergente, Deficiência física, Mobilidade reduzida, Não preciso de suporte. + + Regra: Apenas Usuarios com deficiência podem ser cadastrados no sistema + Cenario de Fundo: + um usuário que recebeu a pergunta Você possui algum tipo de deficiencia? E usuário não cadastrado + + Cenario: O usuário responde que não precisa de suporte + Quando o usuário clicar no botão não preciso de suporte + Então O bot envia a mensagem Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC. + + Cenario: O usuário responde que tem deficiência + Quando o usuário clicar no botão de algum tipo de deficiência que seria: Deficiência visual, Deficiência auditiva/surdez, Surdocegueira, TEA/Neurodivergente, Deficiência física, Mobilidade reduzida, Não preciso de suporte. + Então o bot vai registrar o tipo de deficiência do usuário E bot vai mandar a mensagem “Qual o seu nome?” + + Cenario: O usuário responde com o seu nome + Dado Usuario recebe a mensagem do bot Qual o seu nome? + Quando Usuário mandar qualquer mensagem para o botpcd + Entao o bot precisa Salvar o nome do usuario From 0ceb3477058c189c472b5dda34ee403b61cecac6 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Fri, 30 May 2025 19:57:58 -0300 Subject: [PATCH 40/60] Refactor the registerPCD.feature for not sending buttons --- .../sbpc/botpcd/service/AttendanceService.kt | 42 ------------------- .../sbpc/botpcd/service/RegisterPWDService.kt | 2 +- .../sbpc/botpcd/service/WhatsappService.kt | 28 +------------ .../ufrpe/sbpc/botpcd/registerPCD.feature | 29 ++++++++----- 4 files changed, 20 insertions(+), 81 deletions(-) delete mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt deleted file mode 100644 index 02fd5d1..0000000 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt +++ /dev/null @@ -1,42 +0,0 @@ -package ufrpe.sbpc.botpcd.service - -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import ufrpe.sbpc.botpcd.entity.* -import ufrpe.sbpc.botpcd.repository.AttendanceRepository - -@Service -class AttendanceService( - private val attendanceRepository: AttendanceRepository, - private val userStatusService: AttendantStatusService -) { - - fun searchAttendances(): List { - return attendanceRepository.findAll() - } - - fun getAttendant(attendant: Attendant): Any { - return when (attendant) { - Attendant.MONITOR -> attendance.attendant as Monitor - Attendant.COMMITTEE_MEMBER -> attendance.provider as CommitteeMember - } - } - - @Transactional - fun beginAttendance(attendance: Attendance) { - when (attendance.attendantType) { - Attendant.MONITOR -> userStatusService.setMonitorStatus(attendance.attendant as Monitor, UserStatus.BUSY) - Attendant.COMMITTEE_MEMBER -> userStatusService.setCommitteeMemberStatus(attendance.attendant as CommitteeMember, UserStatus.BUSY) - } - attendanceRepository.save(attendance) - } - - @Transactional - fun endAttendance(attendance: Attendance) { - userStatusService.setMonitorStatus(attendance.monitor, UserStatus.AVAILABLE) - userStatusService.setCommitteeMemberStatus(attendance.committeemember, UserStatus.AVAILABLE) - attendance.endDateTime = java.time.LocalDateTime.now() - attendanceRepository.save(attendance) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt index ee9e976..1634d9c 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt @@ -19,7 +19,7 @@ class RegisterPWDService( } fun whatIsYourDisability(botNumber: String, pwdPhoneNumber: String) { - whatsappService.sendButtons(botNumber,pwdPhoneNumber, Disability.disabilitiesOptions(), "Qual sua deficiência") + whatsappService.sendMessage(botNumber,pwdPhoneNumber, Disability.disabilitiesOptions(), "Qual sua deficiência") } fun whatsIsYourName(pwdPhoneNumber: String) { diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt index f28d28e..ecedafa 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -15,38 +15,12 @@ import org.springframework.stereotype.Service class WhatsappService( private val cloudApi: WhatsappBusinessCloudApi ) { - - fun sendMessage(botNumber: String, destinyNumberID: String, msg: String) { + fun sendMessage(botNumber: String, destinyNumberID: String, msg: String) { val message = Message.MessageBuilder.builder() .setTo(destinyNumberID) .buildTextMessage(TextMessage().setBody(msg)) cloudApi.sendMessage(botNumber, message) } - - fun sendButtons(botNumber: String, destinyNumberID: String, buttons: List, titleMessage: String) { - val section = Section().setTitle("Selecione") - - buttons.forEachIndexed { index, buttonText -> - val row = com.whatsapp.api.domain.messages.Row() - .setId("row_$index") - .setTitle("Opção ${index + 1}") - .setDescription(buttonText) - section.addRow(row) - } - - val interactiveMessage = InteractiveMessage.build() - .setAction(Action().setButtonText(titleMessage).addSection(section)) - .setType(InteractiveMessageType.LIST) - .setHeader(com.whatsapp.api.domain.messages.Header().setType(com.whatsapp.api.domain.messages.type.HeaderType.TEXT).setText(titleMessage)) - .setBody(Body().setText("Selecione seu tipo de deficiẽncia:")) - .setFooter(com.whatsapp.api.domain.messages.Footer().setText("Rodapé")) - - val message = Message.MessageBuilder.builder() - .setTo(destinyNumberID) - .buildInteractiveMessage(interactiveMessage) - - cloudApi.sendMessage(botNumber, message) - } } diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index dcb5bc0..6d6823a 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -2,24 +2,31 @@ Funcionalidade: Cadastro do PCD Como um PCD, quero poder me cadastrar para acessar os serviços da SBPC com mais facilidade. - Cenario: O Usuário não cadastrado manda qualquer mensagem - Dado Um Usuário não cadastrado - Quando Usuário mandar qualquer mensagem para o botpcd - Entao O bot envia a mensagem Olá, qual sua deficiência: E bot envia uma lista de botões com as seguintes deficiências: Deficiência visual, Deficiência auditiva/surdez, Surdocegueira, TEA/Neurodivergente, Deficiência física, Mobilidade reduzida, Não preciso de suporte. + Cenario: Usuário não cadastrado manda qualquer mensagem + Dado usuário não cadastrado + Quando usuário mandar qualquer mensagem para o botpcd + Entao bot envia a mensagem Olá, qual sua deficiência? Digite 1 para Deficiência visual, Digite 2 para Deficiência auditiva/surdez, Digite 3 para Surdocegueira, Digite 4 para TEA/Neurodivergente, Digite 5 para Deficiência física, Digite 6 para Mobilidade reduzida, Digite 7 para Não preciso de suporte. Regra: Apenas Usuarios com deficiência podem ser cadastrados no sistema Cenario de Fundo: - um usuário que recebeu a pergunta Você possui algum tipo de deficiencia? E usuário não cadastrado + usuário que recebeu a mensagem Você possui algum tipo de deficiencia? E usuário não cadastrado Cenario: O usuário responde que não precisa de suporte - Quando o usuário clicar no botão não preciso de suporte + Quando O usuário envia a mensagem 7. Então O bot envia a mensagem Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC. - Cenario: O usuário responde que tem deficiência - Quando o usuário clicar no botão de algum tipo de deficiência que seria: Deficiência visual, Deficiência auditiva/surdez, Surdocegueira, TEA/Neurodivergente, Deficiência física, Mobilidade reduzida, Não preciso de suporte. - Então o bot vai registrar o tipo de deficiência do usuário E bot vai mandar a mensagem “Qual o seu nome?” - + Esquema do Cenario: O usuário responde que tem deficiência + Quando usuario envia a mensagem . + Então o bot vai registrar o tipo de deficiência do usuário E bot vai envia a mensagem Qual o seu nome? + Exemplos: + | numero_da_deficiencia | + | 1 | + | 2 | + | 3 | + | 4 | + | 5 | + | 6 | Cenario: O usuário responde com o seu nome - Dado Usuario recebe a mensagem do bot Qual o seu nome? + Dado usuario recebeu a mensagem do bot Qual o seu nome? Quando Usuário mandar qualquer mensagem para o botpcd Entao o bot precisa Salvar o nome do usuario From b3d9be7238b13df33a5892146fcc5a0f252a5f03 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Fri, 30 May 2025 23:58:32 -0300 Subject: [PATCH 41/60] Fix problem and send a message --- .../ufrpe/sbpc/botpcd/service/RegisterPWDService.kt | 10 +++++++++- .../resources/ufrpe/sbpc/botpcd/registerPCD.feature | 8 ++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt index 1634d9c..eb073ec 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt @@ -19,7 +19,15 @@ class RegisterPWDService( } fun whatIsYourDisability(botNumber: String, pwdPhoneNumber: String) { - whatsappService.sendMessage(botNumber,pwdPhoneNumber, Disability.disabilitiesOptions(), "Qual sua deficiência") + val message = """Olá, qual sua deficiência? + |Digite 1 para Deficiência visual, + |Digite 2 para Deficiência auditiva/surdez, + |Digite 3 para Surdocegueira, + |Digite 4 para TEA/Neurodivergente, + |Digite 5 para Deficiência física, + |Digite 6 para Mobilidade reduzida, + |Digite 7 para Não preciso de suporte.""".trimMargin() + whatsappService.sendMessage(botNumber,pwdPhoneNumber, message) } fun whatsIsYourName(pwdPhoneNumber: String) { diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index 6d6823a..1a7cc41 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -16,7 +16,7 @@ Funcionalidade: Cadastro do PCD Então O bot envia a mensagem Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC. Esquema do Cenario: O usuário responde que tem deficiência - Quando usuario envia a mensagem . + Quando usuário envia a mensagem . Então o bot vai registrar o tipo de deficiência do usuário E bot vai envia a mensagem Qual o seu nome? Exemplos: | numero_da_deficiencia | @@ -27,6 +27,6 @@ Funcionalidade: Cadastro do PCD | 5 | | 6 | Cenario: O usuário responde com o seu nome - Dado usuario recebeu a mensagem do bot Qual o seu nome? - Quando Usuário mandar qualquer mensagem para o botpcd - Entao o bot precisa Salvar o nome do usuario + Dado usuário recebeu a mensagem do bot Qual o seu nome? + Quando usuário mandar qualquer mensagem para o botpcd + Entao o bot precisa Salvar o nome do usuário From dbdfd923658ccd8f4598cbc8ecb8cb9e0b84e95a Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Sat, 31 May 2025 11:30:09 -0300 Subject: [PATCH 42/60] put the mocks files --- build.gradle.kts | 2 +- .../controller/GlobalExceptionHandler.kt | 19 +++++++++ .../controller/WhatsappWebhookController.kt | 8 +++- .../ufrpe/sbpc/botpcd/entity/Attendant.kt | 4 +- .../ufrpe/sbpc/botpcd/entity/Disability.kt | 2 + .../kotlin/ufrpe/sbpc/botpcd/entity/PWD.kt | 3 +- .../kotlin/ufrpe/sbpc/botpcd/entity/User.kt | 3 +- .../botpcd/service/FirstContactService.kt | 28 ++++++++++++- .../sbpc/botpcd/service/RegisterPWDService.kt | 31 +++++++------- .../sbpc/botpcd/RegisterStepDefinitions.kt | 20 ++++++++++ .../ufrpe/sbpc/botpcd/StepDefinitions.kt | 7 ---- .../botpcd/WhatsappBusinessCloudApiMock.kt | 23 +++++++++++ .../ufrpe/sbpc/botpcd/application.properties | 7 ++++ .../sbpc/botpcd/mocks/unknow-api-call.json | 40 +++++++++++++++++++ .../sbpc/botpcd/mocks/user-send-a-number.json | 39 ++++++++++++++++++ .../sbpc/botpcd/mocks/usuario-manda-oi.json | 39 ++++++++++++++++++ .../botpcd/mocks/usuario-manda-seu-nome.json | 39 ++++++++++++++++++ .../ufrpe/sbpc/botpcd/registerPCD.feature | 6 ++- 18 files changed, 284 insertions(+), 36 deletions(-) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/controller/GlobalExceptionHandler.kt create mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt delete mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt create mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt create mode 100644 src/test/resources/ufrpe/sbpc/botpcd/application.properties create mode 100644 src/test/resources/ufrpe/sbpc/botpcd/mocks/unknow-api-call.json create mode 100644 src/test/resources/ufrpe/sbpc/botpcd/mocks/user-send-a-number.json create mode 100644 src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-oi.json create mode 100644 src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-seu-nome.json diff --git a/build.gradle.kts b/build.gradle.kts index ea2d7e6..8e88635 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -50,7 +50,7 @@ dependencies { testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test") testRuntimeOnly("org.junit.platform:junit-platform-launcher") - + testImplementation("io.cucumber:cucumber-picocontainer:7.22.2") testImplementation(platform("org.junit:junit-bom:5.12.2")) testImplementation(platform("io.cucumber:cucumber-bom:7.22.2")) testImplementation(platform("org.assertj:assertj-bom:3.27.3")) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/GlobalExceptionHandler.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/GlobalExceptionHandler.kt new file mode 100644 index 0000000..7f7dfd7 --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/GlobalExceptionHandler.kt @@ -0,0 +1,19 @@ +package ufrpe.sbpc.botpcd.controller + +import org.slf4j.LoggerFactory +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.ControllerAdvice +import org.springframework.web.bind.annotation.ExceptionHandler +import org.slf4j.Logger + +@ControllerAdvice +class GlobalExceptionHandler { + val logger: Logger = LoggerFactory.getLogger(WhatsappWebhookController::class.java) + + @ExceptionHandler(RuntimeException::class) + fun runtimeExceptionHandler(runtimeException: RuntimeException): ResponseEntity> { + logger.error("Erro inesperado ao chamar a API", runtimeException) + return ResponseEntity(mapOf("message" to "Some error ocorrer calling the API"), HttpStatus.INTERNAL_SERVER_ERROR) + } +} \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/WhatsappWebhookController.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/WhatsappWebhookController.kt index 2ef7260..98c030a 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/WhatsappWebhookController.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/WhatsappWebhookController.kt @@ -2,6 +2,7 @@ package ufrpe.sbpc.botpcd.controller import com.whatsapp.api.domain.webhook.WebHook import com.whatsapp.api.domain.webhook.WebHookEvent +import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Value import org.springframework.http.HttpStatus @@ -14,7 +15,7 @@ import ufrpe.sbpc.botpcd.service.FirstContactService class WhatsappWebhookController(private val firstContactService: FirstContactService) { @Value("\${whatsapp.verify.token}") lateinit var VERIFY_TOKEN: String - var logger = LoggerFactory.getLogger(WhatsappWebhookController::class.java) + val logger: Logger = LoggerFactory.getLogger(WhatsappWebhookController::class.java) @PostMapping("/webhooks") fun eventNotification( @@ -27,11 +28,14 @@ class WhatsappWebhookController(private val firstContactService: FirstContactSer val event: WebHookEvent = WebHook.constructEvent(body) for(entry in event.entry) { for(change in entry.changes) { + if(change.value?.messages == null) { + return ResponseEntity.ok("We don't handle this type of message") + } firstContactService.redirectFluxByUserType(change.value.contacts[0].waId, change) } } // Opcional: validar assinatura com 'signature' - return ResponseEntity.ok("Evento processado") + return ResponseEntity.ok("Event process") } @GetMapping("/webhooks") diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendant.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendant.kt index d8802c2..294c025 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendant.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Attendant.kt @@ -13,6 +13,4 @@ abstract class Attendant( phoneNumber: String, @Enumerated(EnumType.STRING) var status: UserStatus = UserStatus.AVAILABLE -) : User(name = name, phoneNumber = phoneNumber) { - -} \ No newline at end of file +) : User(name = name, phoneNumber = phoneNumber) \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt index 53fb979..ce78f47 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt @@ -12,6 +12,7 @@ enum class Disability(val textOption: String) { PHYSICAL_DISABILITY("Deficiência física"), MOBILITY_IMPAIRED("Não tenho deficiência, mas tenho mobilidade reduzida"); + companion object { fun parse(text: String): Disability { val disability = Disability.entries.find { it.textOption.equals(text, ignoreCase = true) } @@ -26,5 +27,6 @@ enum class Disability(val textOption: String) { fun disabilitiesOptions(): List { return listOf(*textList(), "Não preciso de suporte") } + fun getByOrdinal(ordinal: Int) = Disability.entries.find { it.ordinal == ordinal } } } diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWD.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWD.kt index 5ce8c89..45a6a43 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWD.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWD.kt @@ -17,8 +17,7 @@ import org.springframework.data.repository.NoRepositoryBean class PWD( name: String? = null, phoneNumber: String, - phoneNumberId: String, @NotEmpty(message = "The PWD needs to have a disability") @Enumerated(value = EnumType.STRING) var disability: MutableSet -): User(name = name, phoneNumber = phoneNumber, phoneNumberId = phoneNumberId) \ No newline at end of file +): User(name = name, phoneNumber = phoneNumber) \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/User.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/User.kt index 64b65c9..99dda86 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/User.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/User.kt @@ -25,6 +25,5 @@ abstract class User( var id: Long? = null, var name: String? = null, @NotEmpty(message = "The User needs to have a phone number") - var phoneNumber: String, - var phoneNumberId: String? = null + var phoneNumber: String ) \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index d891bbe..79c6486 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -1,7 +1,11 @@ package ufrpe.sbpc.botpcd.service import com.whatsapp.api.domain.webhook.Change +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service +import ufrpe.sbpc.botpcd.controller.WhatsappWebhookController +import ufrpe.sbpc.botpcd.entity.Disability import ufrpe.sbpc.botpcd.repository.CommitteeMemberRepository import ufrpe.sbpc.botpcd.repository.MonitorRepository import ufrpe.sbpc.botpcd.repository.PWDRepository @@ -14,14 +18,22 @@ class FirstContactService( private val registerPWDService: RegisterPWDService, private val whatsappService: WhatsappService ) { + val logger: Logger = LoggerFactory.getLogger(WhatsappWebhookController::class.java) fun redirectFluxByUserType(phoneNumber: String, change: Change) { // to get a message from the change change.value.messages[0].text.body + val disabilityNumberOptions = Disability.entries.map { it.ordinal.toString() }.toMutableList().apply { this.add("7") } val botNumber = change.value.metadata.phoneNumberId + val message = change.value.messages[0].text.body.trim() when { pwdRepository.findByPhoneNumber(phoneNumber) != null -> { val pwd = pwdRepository.findByPhoneNumber(phoneNumber)!! + // Nome ainda não registrado if(pwd.name == null) { -// registerService. + registerPWDService.registerName(pwd, message) + whatsappService.sendMessage(botNumber, phoneNumber, "Cadastro realizado.") + } else { + // Completar com o resto da menssagem + whatsappService.sendMessage(botNumber, phoneNumber, "Olá, ${pwd.name}.") } } monitorRepository.findByPhoneNumber(phoneNumber) != null -> { @@ -29,6 +41,20 @@ class FirstContactService( } committeeMemberRepository.findByPhoneNumber(phoneNumber) != null -> { + } + message in disabilityNumberOptions -> { + val disabilityNumber = message.toInt() + val ordinalDisability = disabilityNumber - 1 + val disability = Disability.getByOrdinal(ordinalDisability) + if(disabilityNumber == 7){ + whatsappService.sendMessage(botNumber, phoneNumber, "Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC.") + } else if(disability == null) { + logger.warn("Foi passado um numero de deficiencia incorreto numero da disability $disabilityNumber") + whatsappService.sendMessage(botNumber, phoneNumber, "Digite um número válido.") + } else { + registerPWDService.registerDisability(botNumber,phoneNumber, disability) + registerPWDService.whatsIsYourName(botNumber, phoneNumber) + } } else -> { // Usuario não cadastrado diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt index eb073ec..64be84d 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt @@ -2,8 +2,6 @@ package ufrpe.sbpc.botpcd.service import org.springframework.stereotype.Service import ufrpe.sbpc.botpcd.repository.PWDRepository -import com.whatsapp.api.impl.WhatsappBusinessCloudApi -import org.springframework.aot.hint.TypeReference.listOf import ufrpe.sbpc.botpcd.entity.Disability import ufrpe.sbpc.botpcd.entity.PWD @@ -12,25 +10,24 @@ class RegisterPWDService( private val pwdRepository: PWDRepository, private val whatsappService: WhatsappService ) { - fun registerDisability(pwdPhoneNumber: String, disability: Disability) { - + fun registerDisability(botPhoneNumber: String, pwdPhoneNumber: String, disability: Disability) { + val pwd = PWD(disability = mutableSetOf(disability), phoneNumber = pwdPhoneNumber) + pwdRepository.save(pwd) } fun registerName(pwd: PWD, name: String) { - + pwd.name = name + pwdRepository.save(pwd) } - fun whatIsYourDisability(botNumber: String, pwdPhoneNumber: String) { - val message = """Olá, qual sua deficiência? - |Digite 1 para Deficiência visual, - |Digite 2 para Deficiência auditiva/surdez, - |Digite 3 para Surdocegueira, - |Digite 4 para TEA/Neurodivergente, - |Digite 5 para Deficiência física, - |Digite 6 para Mobilidade reduzida, - |Digite 7 para Não preciso de suporte.""".trimMargin() - whatsappService.sendMessage(botNumber,pwdPhoneNumber, message) + fun whatIsYourDisability(botPhoneNumber: String, pwdPhoneNumber: String) { + var message = "Olá, qual sua deficiência?\n" + for (disability in Disability.entries) { + message += "Digite ${disability.ordinal + 1} para ${disability.textOption} \n" + } + message += "Digite 7 para Não preciso de suporte." + whatsappService.sendMessage(botPhoneNumber,pwdPhoneNumber, message) } - fun whatsIsYourName(pwdPhoneNumber: String) { - + fun whatsIsYourName(botPhoneNumber: String, pwdPhoneNumber: String) { + whatsappService.sendMessage(botPhoneNumber, pwdPhoneNumber, "Qual o seu nome?") } } diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt new file mode 100644 index 0000000..7cf2e0a --- /dev/null +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt @@ -0,0 +1,20 @@ +package ufrpe.sbpc.botpcd + +import io.cucumber.java.en.Given +import io.cucumber.java.en.Then +import io.cucumber.java.en.When + +class RegisterStepDefinitions { + @Given("usuário não cadastrado") + fun `usuário não cadastrado`() { + + } + @When("usuário mandar qualquer mensagem para o botpcd") + fun `usuário mandar qualquer mensagem para o botpcd`() { + + } + @Then("bot envia a mensagem ") + fun `bot envia a mensagem para o botpcd`() { + + } +} diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt deleted file mode 100644 index 6acb986..0000000 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt +++ /dev/null @@ -1,7 +0,0 @@ -package ufrpe.sbpc.botpcd - -import io.cucumber.java.en.Given - -class StepDefinitions { - -} diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt new file mode 100644 index 0000000..08e4045 --- /dev/null +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt @@ -0,0 +1,23 @@ +package ufrpe.sbpc.botpcd + +import com.whatsapp.api.domain.messages.Contact +import com.whatsapp.api.domain.messages.Message +import com.whatsapp.api.domain.messages.response.MessageResponse +import com.whatsapp.api.impl.WhatsappBusinessCloudApi + +class WhatsappBusinessCloudApiMock(token: String?) : WhatsappBusinessCloudApi(token) { + var capturedPhoneNumberId: String? = null + private set + var capturedMessage: Message? = null + private set + override fun sendMessage(phoneNumberId: String?, message: Message?): MessageResponse { + this.capturedPhoneNumberId = phoneNumberId + this.capturedMessage = message + // Retorna uma resposta simulada + return MessageResponse( + "whatsapp", + listOf(null), + listOf(null) + ); + } +} \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/application.properties b/src/test/resources/ufrpe/sbpc/botpcd/application.properties new file mode 100644 index 0000000..993029c --- /dev/null +++ b/src/test/resources/ufrpe/sbpc/botpcd/application.properties @@ -0,0 +1,7 @@ +spring.application.name=BotPCD +spring.datasource.url=jdbc\:h2\:mem\:testdb\;DB_CLOSE_ON_EXIT\=FALSE\;DB_CLOSE_DELAY\=-1 +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=root +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.jpa.hibernate.ddl-auto=create-drop \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/mocks/unknow-api-call.json b/src/test/resources/ufrpe/sbpc/botpcd/mocks/unknow-api-call.json new file mode 100644 index 0000000..a9a6b2f --- /dev/null +++ b/src/test/resources/ufrpe/sbpc/botpcd/mocks/unknow-api-call.json @@ -0,0 +1,40 @@ +{ + "object": "whatsapp_business_account", + "entry": [ + { + "id": "1944143699658447", + "changes": [ + { + "value": { + "messaging_product": "whatsapp", + "metadata": { + "display_phone_number": "15556557522", + "phone_number_id": "610886818782897" + }, + "statuses": [ + { + "id": "wamid.HBgMNTU4MTg4MTU3OTQ4FQIAERgSQUVCRkM2Qzk4NzJFMDQxN0ExAA==", + "status": "sent", + "timestamp": "1748703539", + "recipient_id": "558112345678", + "conversation": { + "id": "08ac58a1276c34530b921d13182bcb6e", + "expiration_timestamp": "1748735580", + "origin": { + "type": "service" + } + }, + "pricing": { + "billable": true, + "pricing_model": "CBP", + "category": "service" + } + } + ] + }, + "field": "messages" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/mocks/user-send-a-number.json b/src/test/resources/ufrpe/sbpc/botpcd/mocks/user-send-a-number.json new file mode 100644 index 0000000..1a25fff --- /dev/null +++ b/src/test/resources/ufrpe/sbpc/botpcd/mocks/user-send-a-number.json @@ -0,0 +1,39 @@ +{ + "object": "whatsapp_business_account", + "entry": [ + { + "id": "1944143699658447", + "changes": [ + { + "value": { + "messaging_product": "whatsapp", + "metadata": { + "display_phone_number": "15556557522", + "phone_number_id": "610886818782897" + }, + "contacts": [ + { + "profile": { + "name": "joazinho da terra" + }, + "wa_id": "558112345678" + } + ], + "messages": [ + { + "from": "558112345678", + "id": "wamid.jfiajdsijfiqbeufbausdf", + "timestamp": "1748715839", + "text": { + "body": "1" + }, + "type": "text" + } + ] + }, + "field": "messages" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-oi.json b/src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-oi.json new file mode 100644 index 0000000..69ea2c0 --- /dev/null +++ b/src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-oi.json @@ -0,0 +1,39 @@ +{ + "object": "whatsapp_business_account", + "entry": [ + { + "id": "1944143699658447", + "changes": [ + { + "value": { + "messaging_product": "whatsapp", + "metadata": { + "display_phone_number": "15556557522", + "phone_number_id": "610886818782897" + }, + "contacts": [ + { + "profile": { + "name": "joazinho da terra" + }, + "wa_id": "558112345678" + } + ], + "messages": [ + { + "from": "558112345678", + "id": "wamid.Hjjhjbbiunub", + "timestamp": "1748701962", + "text": { + "body": "Oi" + }, + "type": "text" + } + ] + }, + "field": "messages" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-seu-nome.json b/src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-seu-nome.json new file mode 100644 index 0000000..1abd245 --- /dev/null +++ b/src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-seu-nome.json @@ -0,0 +1,39 @@ +{ + "object": "whatsapp_business_account", + "entry": [ + { + "id": "1944143699658447", + "changes": [ + { + "value": { + "messaging_product": "whatsapp", + "metadata": { + "display_phone_number": "15556557522", + "phone_number_id": "610886818782897" + }, + "contacts": [ + { + "profile": { + "name": "Joaozinho da terra" + }, + "wa_id": "558112345678" + } + ], + "messages": [ + { + "from": "558112345678", + "id": "wamidohutydrtctg", + "timestamp": "1748717220", + "text": { + "body": "joazinho da terra" + }, + "type": "text" + } + ] + }, + "field": "messages" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index 1a7cc41..4c2069a 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -15,6 +15,10 @@ Funcionalidade: Cadastro do PCD Quando O usuário envia a mensagem 7. Então O bot envia a mensagem Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC. + Cenario: + Quando o usuário envia mensagem 10 + Entao bot envia a mensagem Olá, qual sua deficiência? Digite 1 para Deficiência visual, Digite 2 para Deficiência auditiva/surdez, Digite 3 para Surdocegueira, Digite 4 para TEA/Neurodivergente, Digite 5 para Deficiência física, Digite 6 para Mobilidade reduzida, Digite 7 para Não preciso de suporte. + Esquema do Cenario: O usuário responde que tem deficiência Quando usuário envia a mensagem . Então o bot vai registrar o tipo de deficiência do usuário E bot vai envia a mensagem Qual o seu nome? @@ -29,4 +33,4 @@ Funcionalidade: Cadastro do PCD Cenario: O usuário responde com o seu nome Dado usuário recebeu a mensagem do bot Qual o seu nome? Quando usuário mandar qualquer mensagem para o botpcd - Entao o bot precisa Salvar o nome do usuário + Entao o bot precisa Salvar o nome do usuário E o bot vai envia a mensagem Cadastro realizado. From 3e46508e0cff8aa9f64ea16390f154f3ca907287 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Sat, 31 May 2025 22:05:36 -0300 Subject: [PATCH 43/60] Write the first feature test --- build.gradle.kts | 2 +- .../sbpc/botpcd/config/WhatsappApiConfig.kt | 2 ++ .../ufrpe/sbpc/botpcd/entity/Disability.kt | 12 +++++---- .../sbpc/botpcd/service/RegisterPWDService.kt | 7 +---- .../sbpc/botpcd/RegisterStepDefinitions.kt | 26 ++++++++++++++++--- .../CucumberSpringContextConfiguration.kt | 13 ++++++++++ .../ufrpe/sbpc/botpcd/config/MockConfig.kt | 17 ++++++++++++ .../sbpc/botpcd => }/application.properties | 4 ++- .../ufrpe/sbpc/botpcd/registerPCD.feature | 6 ++--- 9 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/config/CucumberSpringContextConfiguration.kt create mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/config/MockConfig.kt rename src/test/resources/{ufrpe/sbpc/botpcd => }/application.properties (75%) diff --git a/build.gradle.kts b/build.gradle.kts index 8e88635..cee9f87 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -50,7 +50,7 @@ dependencies { testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test") testRuntimeOnly("org.junit.platform:junit-platform-launcher") - testImplementation("io.cucumber:cucumber-picocontainer:7.22.2") + testImplementation("io.cucumber:cucumber-spring:7.22.2") testImplementation(platform("org.junit:junit-bom:5.12.2")) testImplementation(platform("io.cucumber:cucumber-bom:7.22.2")) testImplementation(platform("org.assertj:assertj-bom:3.27.3")) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/config/WhatsappApiConfig.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/config/WhatsappApiConfig.kt index 673f2bb..1c7db0e 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/config/WhatsappApiConfig.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/config/WhatsappApiConfig.kt @@ -6,8 +6,10 @@ import com.whatsapp.api.impl.WhatsappBusinessCloudApi import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Profile import java.beans.BeanProperty +@Profile("!test") @Configuration class WhatsappApiConfig { @Value("\${whatsapp.api.token}") diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt index ce78f47..576c764 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt @@ -21,11 +21,13 @@ enum class Disability(val textOption: String) { } return disability } - fun textList(): Array { - return Disability.entries.map { it.textOption }.toTypedArray() - } - fun disabilitiesOptions(): List { - return listOf(*textList(), "Não preciso de suporte") + fun getOptions(): String { + var message = "Olá, qual sua deficiência?\n" + for (disability in Disability.entries) { + message += "Digite ${disability.ordinal + 1} para ${disability.textOption} \n" + } + message += "Digite 7 para Não preciso de suporte." + return message } fun getByOrdinal(ordinal: Int) = Disability.entries.find { it.ordinal == ordinal } } diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt index 64be84d..7815f19 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt @@ -19,12 +19,7 @@ class RegisterPWDService( pwdRepository.save(pwd) } fun whatIsYourDisability(botPhoneNumber: String, pwdPhoneNumber: String) { - var message = "Olá, qual sua deficiência?\n" - for (disability in Disability.entries) { - message += "Digite ${disability.ordinal + 1} para ${disability.textOption} \n" - } - message += "Digite 7 para Não preciso de suporte." - whatsappService.sendMessage(botPhoneNumber,pwdPhoneNumber, message) + whatsappService.sendMessage(botPhoneNumber,pwdPhoneNumber, Disability.getOptions()) } fun whatsIsYourName(botPhoneNumber: String, pwdPhoneNumber: String) { diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt index 7cf2e0a..3870cf2 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt @@ -3,18 +3,36 @@ package ufrpe.sbpc.botpcd import io.cucumber.java.en.Given import io.cucumber.java.en.Then import io.cucumber.java.en.When +import org.junit.jupiter.api.Assertions.assertEquals +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import ufrpe.sbpc.botpcd.entity.Disability +import java.io.File class RegisterStepDefinitions { + @Autowired + private lateinit var mockMvc: MockMvc + @Autowired + private lateinit var whatsappBusinessCloudApiMock: WhatsappBusinessCloudApiMock @Given("usuário não cadastrado") fun `usuário não cadastrado`() { } @When("usuário mandar qualquer mensagem para o botpcd") fun `usuário mandar qualquer mensagem para o botpcd`() { - + val payload = File("src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-oi.json").readText(Charsets.UTF_8) + mockMvc.perform( + post("/webhooks") + .content(payload) + .contentType("application/json") + ) } - @Then("bot envia a mensagem ") - fun `bot envia a mensagem para o botpcd`() { - + @Then("^bot envia mensagens com o tipo de deficiencia$") + fun `bot envia qualquer mensagem`() { + val mensagemEsperada = Disability.getOptions() + val apiMock = whatsappBusinessCloudApiMock + val mensagemEnviada = apiMock.capturedMessage!!.textMessage!!.body + assertEquals(mensagemEnviada, mensagemEsperada) } } diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/config/CucumberSpringContextConfiguration.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/config/CucumberSpringContextConfiguration.kt new file mode 100644 index 0000000..2425e2c --- /dev/null +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/config/CucumberSpringContextConfiguration.kt @@ -0,0 +1,13 @@ +package ufrpe.sbpc.botpcd.config + +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.context.web.WebAppConfiguration +import io.cucumber.spring.CucumberContextConfiguration +import org.springframework.test.context.ActiveProfiles + +@CucumberContextConfiguration +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) +@AutoConfigureMockMvc +@ActiveProfiles("test") +class CucumberSpringContextConfiguration \ No newline at end of file diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/config/MockConfig.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/config/MockConfig.kt new file mode 100644 index 0000000..a0e218f --- /dev/null +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/config/MockConfig.kt @@ -0,0 +1,17 @@ +package ufrpe.sbpc.botpcd.config + +import com.whatsapp.api.impl.WhatsappBusinessCloudApi +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Primary +import org.springframework.context.annotation.Profile +import ufrpe.sbpc.botpcd.WhatsappBusinessCloudApiMock + +@Configuration +@Profile("test") +class MockConfig { + @Bean + fun whatsappMock(): WhatsappBusinessCloudApi { + return WhatsappBusinessCloudApiMock("fakeToken") + } +} \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/application.properties b/src/test/resources/application.properties similarity index 75% rename from src/test/resources/ufrpe/sbpc/botpcd/application.properties rename to src/test/resources/application.properties index 993029c..f72463b 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/application.properties +++ b/src/test/resources/application.properties @@ -4,4 +4,6 @@ spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.password=root spring.jpa.database-platform=org.hibernate.dialect.H2Dialect -spring.jpa.hibernate.ddl-auto=create-drop \ No newline at end of file +spring.jpa.hibernate.ddl-auto=create-drop +whatsapp.api.token=teste +whatsapp.verify.token=teste \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index 4c2069a..738adad 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -5,7 +5,7 @@ Funcionalidade: Cadastro do PCD Cenario: Usuário não cadastrado manda qualquer mensagem Dado usuário não cadastrado Quando usuário mandar qualquer mensagem para o botpcd - Entao bot envia a mensagem Olá, qual sua deficiência? Digite 1 para Deficiência visual, Digite 2 para Deficiência auditiva/surdez, Digite 3 para Surdocegueira, Digite 4 para TEA/Neurodivergente, Digite 5 para Deficiência física, Digite 6 para Mobilidade reduzida, Digite 7 para Não preciso de suporte. + Entao bot envia mensagens com o tipo de deficiencia Regra: Apenas Usuarios com deficiência podem ser cadastrados no sistema Cenario de Fundo: @@ -13,7 +13,7 @@ Funcionalidade: Cadastro do PCD Cenario: O usuário responde que não precisa de suporte Quando O usuário envia a mensagem 7. - Então O bot envia a mensagem Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC. + Então bot envia a mensagem Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC. Cenario: Quando o usuário envia mensagem 10 @@ -33,4 +33,4 @@ Funcionalidade: Cadastro do PCD Cenario: O usuário responde com o seu nome Dado usuário recebeu a mensagem do bot Qual o seu nome? Quando usuário mandar qualquer mensagem para o botpcd - Entao o bot precisa Salvar o nome do usuário E o bot vai envia a mensagem Cadastro realizado. + Entao bot envia a mensagem Cadastro realizado E bot salva o nome do usuário From c46285b9e5eea1d57e66dcc66d85ca123cba6ea4 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Sat, 31 May 2025 22:10:32 -0300 Subject: [PATCH 44/60] Harded code the text --- .../ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt index 3870cf2..87eea61 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt @@ -30,7 +30,15 @@ class RegisterStepDefinitions { } @Then("^bot envia mensagens com o tipo de deficiencia$") fun `bot envia qualquer mensagem`() { - val mensagemEsperada = Disability.getOptions() + val mensagemEsperada = """Olá, qual sua deficiência? +Digite 1 para Deficiência visual +Digite 2 para Deficiência auditiva/surdez +Digite 3 para Surdocegueira +Digite 4 para Transtorno do Espectro Autista/Neurodivergente +Digite 5 para Deficiência física +Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida +Digite 7 para Não preciso de suporte. + """.trimIndent() val apiMock = whatsappBusinessCloudApiMock val mensagemEnviada = apiMock.capturedMessage!!.textMessage!!.body assertEquals(mensagemEnviada, mensagemEsperada) From b6fc87e5d4b1f3363b2af06062a5e11f1803d185 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Sun, 1 Jun 2025 13:47:47 -0300 Subject: [PATCH 45/60] Improve the steps definitions --- .../botpcd/service/FirstContactService.kt | 1 + .../ufrpe/sbpc/botpcd/PayloadMockFactory.kt | 33 +++++++++++++ .../sbpc/botpcd/RegisterStepDefinitions.kt | 48 ++++++++++++++----- .../botpcd/WhatsappBusinessCloudApiMock.kt | 12 +++++ .../sbpc/botpcd/entity/MessageExchange.kt | 23 +++++++++ .../repository/MessageExchangeRepository.kt | 8 ++++ .../ufrpe/sbpc/botpcd/registerPCD.feature | 5 +- 7 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/PayloadMockFactory.kt create mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt create mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index 79c6486..8cd820b 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -53,6 +53,7 @@ class FirstContactService( whatsappService.sendMessage(botNumber, phoneNumber, "Digite um número válido.") } else { registerPWDService.registerDisability(botNumber,phoneNumber, disability) + whatsappService.sendMessage(botNumber, phoneNumber, "Entendi que você ${disability.textOption}") registerPWDService.whatsIsYourName(botNumber, phoneNumber) } } diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/PayloadMockFactory.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/PayloadMockFactory.kt new file mode 100644 index 0000000..5c37b01 --- /dev/null +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/PayloadMockFactory.kt @@ -0,0 +1,33 @@ +package ufrpe.sbpc.botpcd + +import com.whatsapp.api.domain.webhook.Change +import com.whatsapp.api.domain.webhook.Value +import com.whatsapp.api.domain.webhook.WebHook +import org.hibernate.sql.ast.tree.insert.Values +import org.springframework.stereotype.Service +import java.io.File + +@Service +class PayloadMockFactory { + + fun loadPayload(filePath: String): String { + return File(filePath).readText(Charsets.UTF_8) + } + + fun changeUserNumber(payload: String, newNumber: String): String { + return payload.replace(Regex("(?<=\"wa_id\": \")\\d+(?=\")"), newNumber) + } + + fun changeUserMessage(payload: String, newMessage: String): String { + // Exemplo simples substituindo o texto da mensagem. + // Ajuste de acordo com a estrutura do JSON. + return payload.replace(Regex("(?<=\\\"body\\\": \\\").*?(?=\\\")"), newMessage) + } + fun getBotNumber(payload: String): String { + return getChange(payload).metadata.displayPhoneNumber + } + fun getChange(payload: String): Value { + return WebHook.constructEvent(payload).entry[0].changes[0].value + } + fun getMessage(payload: String) = getChange(payload).messages[0].text.body +} \ No newline at end of file diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt index 87eea61..e398cf3 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt @@ -1,5 +1,7 @@ package ufrpe.sbpc.botpcd +import com.fasterxml.jackson.databind.json.JsonMapper +import com.whatsapp.api.domain.webhook.WebHook import io.cucumber.java.en.Given import io.cucumber.java.en.Then import io.cucumber.java.en.When @@ -7,30 +9,47 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.springframework.beans.factory.annotation.Autowired import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import ufrpe.sbpc.botpcd.entity.Disability +import ufrpe.sbpc.botpcd.entity.MessageExchange +import ufrpe.sbpc.botpcd.repository.MessageExchangeRepository +import ufrpe.sbpc.botpcd.repository.PWDRepository import java.io.File -class RegisterStepDefinitions { - @Autowired - private lateinit var mockMvc: MockMvc - @Autowired - private lateinit var whatsappBusinessCloudApiMock: WhatsappBusinessCloudApiMock +class RegisterStepDefinitions( + private val mockMvc: MockMvc, + val whatsappBusinessCloudApiMock: WhatsappBusinessCloudApiMock, + val pwdRepository: PWDRepository, + val payloadMockFactory: PayloadMockFactory, + val messageExchangeRepository: MessageExchangeRepository, +) { + private val numberUserNotRegister: String = "558187654321" @Given("usuário não cadastrado") fun `usuário não cadastrado`() { - + assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister), null) } + @When("usuário mandar qualquer mensagem para o botpcd") fun `usuário mandar qualquer mensagem para o botpcd`() { - val payload = File("src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-oi.json").readText(Charsets.UTF_8) + var payload = payloadMockFactory.loadPayload("src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-oi.json") + payload = payloadMockFactory.changeUserNumber(payload, numberUserNotRegister) mockMvc.perform( post("/webhooks") .content(payload) .contentType("application/json") - ) + ).andExpect(status().isOk) + messageExchangeRepository.save(MessageExchange( + fromNumber = numberUserNotRegister, + toNumber = payloadMockFactory.getBotNumber(payload), + message = payloadMockFactory.getMessage(payload) + )) } - @Then("^bot envia mensagens com o tipo de deficiencia$") - fun `bot envia qualquer mensagem`() { - val mensagemEsperada = """Olá, qual sua deficiência? + + @Then("bot envia mensagem {string}") + fun `bot envia mensagem`(message: String) { + var mensagemEsperada = "" + if(message == "com os tipos de deficiencias") { + mensagemEsperada = """Olá, qual sua deficiência? Digite 1 para Deficiência visual Digite 2 para Deficiência auditiva/surdez Digite 3 para Surdocegueira @@ -39,8 +58,15 @@ Digite 5 para Deficiência física Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida Digite 7 para Não preciso de suporte. """.trimIndent() + } else { + mensagemEsperada = message + } val apiMock = whatsappBusinessCloudApiMock val mensagemEnviada = apiMock.capturedMessage!!.textMessage!!.body assertEquals(mensagemEnviada, mensagemEsperada) } + @Given("usuário que recebeu a mensagem {string}") + fun `usuário que recebeu a mensagem`(message: String) { + + } } diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt index 08e4045..0a5a6bc 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt @@ -4,16 +4,28 @@ import com.whatsapp.api.domain.messages.Contact import com.whatsapp.api.domain.messages.Message import com.whatsapp.api.domain.messages.response.MessageResponse import com.whatsapp.api.impl.WhatsappBusinessCloudApi +import org.springframework.beans.factory.annotation.Autowired +import ufrpe.sbpc.botpcd.entity.MessageExchange +import ufrpe.sbpc.botpcd.repository.MessageExchangeRepository class WhatsappBusinessCloudApiMock(token: String?) : WhatsappBusinessCloudApi(token) { var capturedPhoneNumberId: String? = null private set var capturedMessage: Message? = null private set + @Autowired + private lateinit var messageExchangeRepository: MessageExchangeRepository override fun sendMessage(phoneNumberId: String?, message: Message?): MessageResponse { this.capturedPhoneNumberId = phoneNumberId this.capturedMessage = message // Retorna uma resposta simulada + messageExchangeRepository.save( + MessageExchange( + fromNumber = phoneNumberId!!, + toNumber = message!!.to, + message = message.textMessage.body + ) + ) return MessageResponse( "whatsapp", listOf(null), diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt new file mode 100644 index 0000000..717fbba --- /dev/null +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt @@ -0,0 +1,23 @@ +package ufrpe.sbpc.botpcd.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import org.hibernate.annotations.CreationTimestamp +import java.time.LocalDateTime + +@Entity +class MessageExchange( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + var fromNumber: String, + var toNumber: String, + @Column(columnDefinition = "TEXT") + var message: String +) { + @CreationTimestamp + var createAt: LocalDateTime? = null +} \ No newline at end of file diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt new file mode 100644 index 0000000..d720f65 --- /dev/null +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt @@ -0,0 +1,8 @@ +package ufrpe.sbpc.botpcd.repository + +import org.springframework.data.jpa.repository.JpaRepository +import ufrpe.sbpc.botpcd.entity.MessageExchange + +interface MessageExchangeRepository: JpaRepository { + +} \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index 738adad..9010812 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -5,11 +5,10 @@ Funcionalidade: Cadastro do PCD Cenario: Usuário não cadastrado manda qualquer mensagem Dado usuário não cadastrado Quando usuário mandar qualquer mensagem para o botpcd - Entao bot envia mensagens com o tipo de deficiencia + Entao bot envia mensagem "com os tipos de deficiencias" Regra: Apenas Usuarios com deficiência podem ser cadastrados no sistema - Cenario de Fundo: - usuário que recebeu a mensagem Você possui algum tipo de deficiencia? E usuário não cadastrado + Cenário de Fundo: usuário que recebeu a mensagem com os tipos de deficiencias E usuário não cadastrado Cenario: O usuário responde que não precisa de suporte Quando O usuário envia a mensagem 7. From 92f2839f4e500deb1edb56ef8459dfe9577d8750 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Sun, 1 Jun 2025 17:53:17 -0300 Subject: [PATCH 46/60] WIP only instanciate the MessageExchangeRepository.kt in the test context --- .../sbpc/botpcd/RegisterStepDefinitions.kt | 2 +- .../sbpc/botpcd/entity/MessageExchange.kt | 2 ++ .../repository/MessageExchangeRepository.kt | 6 +++++- .../ufrpe/sbpc/botpcd/registerPCD.feature | 18 +++++++++--------- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt index e398cf3..266a3f9 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt @@ -67,6 +67,6 @@ Digite 7 para Não preciso de suporte. } @Given("usuário que recebeu a mensagem {string}") fun `usuário que recebeu a mensagem`(message: String) { - + val latestMessage = messageExchangeRepository.findLatestMessage() } } diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt index 717fbba..f772349 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt @@ -6,9 +6,11 @@ import jakarta.persistence.GeneratedValue import jakarta.persistence.GenerationType import jakarta.persistence.Id import org.hibernate.annotations.CreationTimestamp +import org.springframework.context.annotation.Profile import java.time.LocalDateTime @Entity +@Profile("test") class MessageExchange( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt index d720f65..a88144f 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt @@ -1,8 +1,12 @@ package ufrpe.sbpc.botpcd.repository +import org.hibernate.annotations.processing.SQL +import org.springframework.context.annotation.Profile import org.springframework.data.jpa.repository.JpaRepository import ufrpe.sbpc.botpcd.entity.MessageExchange +@Profile("test") interface MessageExchangeRepository: JpaRepository { - + @SQL("SELECT * FROM MessageExchange ORDER BY createAt ASC LIMIT 1") + fun findLatestMessage(): MessageExchange? } \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index 9010812..d04cf0d 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -8,19 +8,19 @@ Funcionalidade: Cadastro do PCD Entao bot envia mensagem "com os tipos de deficiencias" Regra: Apenas Usuarios com deficiência podem ser cadastrados no sistema - Cenário de Fundo: usuário que recebeu a mensagem com os tipos de deficiencias E usuário não cadastrado + Cenário de Fundo: usuário que recebeu a mensagem "com os tipos de deficiencias" E usuário não cadastrado Cenario: O usuário responde que não precisa de suporte - Quando O usuário envia a mensagem 7. - Então bot envia a mensagem Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC. + Quando O usuário envia a mensagem "7" + Então bot envia a mensagem "Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC." Cenario: - Quando o usuário envia mensagem 10 - Entao bot envia a mensagem Olá, qual sua deficiência? Digite 1 para Deficiência visual, Digite 2 para Deficiência auditiva/surdez, Digite 3 para Surdocegueira, Digite 4 para TEA/Neurodivergente, Digite 5 para Deficiência física, Digite 6 para Mobilidade reduzida, Digite 7 para Não preciso de suporte. + Quando o usuário envia mensagem "10" + Entao bot envia mensagem "com os tipos de deficiencias" Esquema do Cenario: O usuário responde que tem deficiência - Quando usuário envia a mensagem . - Então o bot vai registrar o tipo de deficiência do usuário E bot vai envia a mensagem Qual o seu nome? + Quando usuário envia a mensagem "" + Então o bot vai registrar o tipo de deficiência do usuário E bot vai envia a mensagem "Qual o seu nome?" Exemplos: | numero_da_deficiencia | | 1 | @@ -30,6 +30,6 @@ Funcionalidade: Cadastro do PCD | 5 | | 6 | Cenario: O usuário responde com o seu nome - Dado usuário recebeu a mensagem do bot Qual o seu nome? + Dado usuário recebeu a mensagem do bot "Qual o seu nome?" Quando usuário mandar qualquer mensagem para o botpcd - Entao bot envia a mensagem Cadastro realizado E bot salva o nome do usuário + Entao bot envia a mensagem "Cadastro realizado" E bot salva o nome do usuário From f8b14209b3adaf932dd1767e485f4d4471d12f3c Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Mon, 2 Jun 2025 11:30:19 -0300 Subject: [PATCH 47/60] WIP remove the test profile --- src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt | 1 - .../ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt index f772349..58efb3d 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt @@ -10,7 +10,6 @@ import org.springframework.context.annotation.Profile import java.time.LocalDateTime @Entity -@Profile("test") class MessageExchange( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt index a88144f..b0483b4 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt @@ -5,7 +5,7 @@ import org.springframework.context.annotation.Profile import org.springframework.data.jpa.repository.JpaRepository import ufrpe.sbpc.botpcd.entity.MessageExchange -@Profile("test") + interface MessageExchangeRepository: JpaRepository { @SQL("SELECT * FROM MessageExchange ORDER BY createAt ASC LIMIT 1") fun findLatestMessage(): MessageExchange? From 60281be1c8d91edcb8a4d4ee5b821926704af19f Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Mon, 2 Jun 2025 19:34:33 -0300 Subject: [PATCH 48/60] WIP create the service and the FirstContactService.kt --- .../sbpc/botpcd/service/AttendantService.kt | 8 ++++ .../botpcd/service/FirstContactService.kt | 5 +- .../ufrpe/sbpc/botpcd/acessarServicos.feature | 46 +++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantService.kt create mode 100644 src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantService.kt new file mode 100644 index 0000000..218ad06 --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantService.kt @@ -0,0 +1,8 @@ +package ufrpe.sbpc.botpcd.service + +import org.springframework.stereotype.Service + +@Service +class AttendantService() { + +} \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index 8cd820b..145ce77 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -36,10 +36,7 @@ class FirstContactService( whatsappService.sendMessage(botNumber, phoneNumber, "Olá, ${pwd.name}.") } } - monitorRepository.findByPhoneNumber(phoneNumber) != null -> { - - } - committeeMemberRepository.findByPhoneNumber(phoneNumber) != null -> { + monitorRepository.findByPhoneNumber(phoneNumber) != null || committeeMemberRepository.findByPhoneNumber(phoneNumber) != null-> { } message in disabilityNumberOptions -> { diff --git a/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature b/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature new file mode 100644 index 0000000..4db3968 --- /dev/null +++ b/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature @@ -0,0 +1,46 @@ +# language: pt +Funcionalidade: Acessar serviços de assistência + Como um PCD, quero poder acessar os serviços que estão disponíveis para o meu tipo de deficiência, para que eu consiga aproveitar o evento da SBPC. + + Cenário de Fundo: pcd está cadastrado + + Esquema do Cenário Listar serviços de acordo com meu tipo de deficiência + Quando O PCD com a deficiência de mandar qualquer mensagem + Entao O bot vai enviar uma lista de opções de acordo com a + + Exemplos: + | tipo_de_deficiência | opccoes_de_servico | + | Surdez | Informações em Libras,atividade com interpretação em Libras | + | Mobilidade Reduzida | ajuda na mobilidade,transporte para deslocamento no evento | + | Deficiência Física | ajuda na mobilidade,ajuda com alimentação e higiene,transporte para deslocamento no evento | + | Cegueira | ajuda na mobilidade,programação com audiodescrição | + | Neurodivergente | suporte para pessoas neurodivergentes | + | Surdocegueira | guia-intérprete | + + Esquema do Cenário Direcionar para o atendente(monitor ou membro da comissão) + Dado o bot enviou a mensagem de opção de serviço + Quando o PCD selecionar o + Entao bot vai direcionar para o disponível no momento + + Exemplos: + | servico_desejado | tipo_de_atendente | + | Informações em Libras | monitor | + | 1 Intérprete de Libras | membro da comissão | + | 2 Assistência mobilidade | monitor | + | 3 carro | membro da comissão | + | 4 Higiene e Nutrição | membro da comissão | + | 5 Audiodescrição | membro da comissão | + | 6 Apoio Neurodivergente | monitor | + | 7 Guia-intérprete | membro da comissão | + + Cenário: Bot direciona mensagem do PCD para o atendente correto + Dado Atendente aceitou o atendimento do PCD + Quando o PCD envia uma mensagem para o bot + Então o Atendente deve receber a mensagem do PCD + E NENHUM outro Atendente ou PCD deve receber a mesma mensagem + + Cenário: Bot direciona mensagem do atendente para o PCD correto + Dado que o Atendente aceitou o atendimento do PCD + Quando o Atendente envia uma mensagem para o bot + Então o PCD deve receber a mensagem do Atendente + E NENHUM outro PCD ou Atendente deve receber a mesma mensagem From a0fe25308beded904a607c196eaa14b73affc2a9 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Mon, 2 Jun 2025 21:29:04 -0300 Subject: [PATCH 49/60] feat: Add support to acessibility tasks Co-authored-by: Emmanuel N. C. Brito --- .../sbpc/botpcd/service/AttendanceService.kt | 18 ++++++++++++++++++ .../sbpc/botpcd/service/AttendantService.kt | 8 -------- 2 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt delete mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantService.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt new file mode 100644 index 0000000..10ca497 --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendanceService.kt @@ -0,0 +1,18 @@ +package ufrpe.sbpc.botpcd.service + +import org.springframework.stereotype.Service +import ufrpe.sbpc.botpcd.entity.Disability +import ufrpe.sbpc.botpcd.entity.ServiceType +import ufrpe.sbpc.botpcd.repository.AttendanceRepository + +@Service +class AttendanceService( + private val attendanceRepository: AttendanceRepository, + private val whatsappService: WhatsappService, +) { + fun sendServices(botNumber: String, userNumber: String, disability: Disability) { + val serviceList = ServiceType.getServicesByDisability(disability) + val message = whatsappService.createOptions(serviceList.map { it -> it.description }) + whatsappService.sendMessage(botNumber, userNumber, message) + } +} \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantService.kt deleted file mode 100644 index 218ad06..0000000 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/AttendantService.kt +++ /dev/null @@ -1,8 +0,0 @@ -package ufrpe.sbpc.botpcd.service - -import org.springframework.stereotype.Service - -@Service -class AttendantService() { - -} \ No newline at end of file From 6bbeed815764af4fc4062cbbd8e9336e99daadc0 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Mon, 2 Jun 2025 21:30:49 -0300 Subject: [PATCH 50/60] feat: Add functino to create options list in whatsapp service Co-authored-by: Emmanuel N. C. Brito --- .../ufrpe/sbpc/botpcd/service/WhatsappService.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt index ecedafa..388b0cf 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -15,12 +15,20 @@ import org.springframework.stereotype.Service class WhatsappService( private val cloudApi: WhatsappBusinessCloudApi ) { - fun sendMessage(botNumber: String, destinyNumberID: String, msg: String) { + fun sendMessage(botNumber: String, destinyNumberID: String, msg: String) { val message = Message.MessageBuilder.builder() .setTo(destinyNumberID) .buildTextMessage(TextMessage().setBody(msg)) cloudApi.sendMessage(botNumber, message) } + + fun createOptions(options: List, header: String = ""): String { + var mensage = "${header}\n" + for (i in options.indices) { + mensage += "- Digite ${i + 1} para ${options[i]}\n" + } + return mensage + } } From cf39309293ddc2896647d857bf09576e1191c846 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Mon, 2 Jun 2025 21:43:37 -0300 Subject: [PATCH 51/60] WIP improve the features --- .../ufrpe/sbpc/botpcd/entity/ServiceType.kt | 70 +++++++++++++++---- .../botpcd/service/FirstContactService.kt | 4 +- .../sbpc/botpcd/entity/MessageExchange.kt | 24 ------- .../repository/MessageExchangeRepository.kt | 12 ---- .../ufrpe/sbpc/botpcd/acessarServicos.feature | 4 +- .../ufrpe/sbpc/botpcd/registerPCD.feature | 2 +- 6 files changed, 64 insertions(+), 52 deletions(-) delete mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt delete mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/ServiceType.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/ServiceType.kt index 30590d1..9d90e05 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/ServiceType.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/ServiceType.kt @@ -3,23 +3,69 @@ package ufrpe.sbpc.botpcd.entity /** * */ -sealed class ServiceType(val provider: Provider, val disability: Set) { - object Libras : ServiceType(provider = Provider.MONITOR, disability = mutableSetOf(Disability.DEAFNESS)) { +sealed class ServiceType( + val provider: Provider, + val disability: Set, + val description: String +) { + object Libras : ServiceType( + provider = Provider.MONITOR, + disability = mutableSetOf(Disability.DEAFNESS), + description = "informações em Libras" + ) { val monitorAssistanceType = MonitorAssistanceType.LIBRAS_MONITOR } - object LibrasInterpreter: ServiceType(provider = Provider.COMMITTEE_MEMBER, disability = mutableSetOf(Disability.DEAFNESS)) - object Mobility: ServiceType(provider = Provider.MONITOR, disability = mutableSetOf(Disability.MOBILITY_IMPAIRED, Disability.PHYSICAL_DISABILITY, Disability.BLINDED)) { + object LibrasInterpreter : ServiceType( + provider = Provider.COMMITTEE_MEMBER, + disability = mutableSetOf(Disability.DEAFNESS), + description = "atividade com interpretação em Libras" + ) + object Mobility : ServiceType( + provider = Provider.MONITOR, + disability = mutableSetOf(Disability.MOBILITY_IMPAIRED, Disability.PHYSICAL_DISABILITY, Disability.BLINDED), + description = "ajuda na mobilidade" + ) { val monitorAssistanceType = MonitorAssistanceType.MOBILITY_MONITOR } - object AudioDescription: ServiceType(provider = Provider.COMMITTEE_MEMBER, disability = mutableSetOf(Disability.BLINDED)) - object NeurodivergentSupport: ServiceType(provider = Provider.MONITOR, disability = mutableSetOf(Disability.NEURODIVERGENT)) { + + object AudioDescription : ServiceType( + provider = Provider.COMMITTEE_MEMBER, + disability = mutableSetOf(Disability.BLINDED), + description = "programação com audiodescrição" + ) + + object NeurodivergentSupport : ServiceType( + provider = Provider.MONITOR, + disability = mutableSetOf(Disability.NEURODIVERGENT), + description = "suporte para pessoas neurodivergentes" + ) { val monitorAssistanceType = MonitorAssistanceType.NEURODIVERGENT_SUPPORT_MONITOR } - object GuideInterpreter: ServiceType(provider = Provider.COMMITTEE_MEMBER, disability = mutableSetOf(Disability.DEAFBLINDNESS)) - object HygieneAndNutrition: ServiceType(provider = Provider.COMMITTEE_MEMBER, disability = mutableSetOf(Disability.MOBILITY_IMPAIRED, Disability.PHYSICAL_DISABILITY)) - object Car : ServiceType(provider = Provider.COMMITTEE_MEMBER, disability = mutableSetOf(Disability.PHYSICAL_DISABILITY, Disability.MOBILITY_IMPAIRED - )); - fun getServicesByDisability(disability: Disability): List { - return ServiceType::class.sealedSubclasses.mapNotNull{ it.objectInstance }.filter{ disability in it.disability } + + object GuideInterpreter : ServiceType( + provider = Provider.COMMITTEE_MEMBER, + disability = mutableSetOf(Disability.DEAFBLINDNESS), + description = "guia-intérprete" + ) + + object HygieneAndNutrition : ServiceType( + provider = Provider.COMMITTEE_MEMBER, + disability = mutableSetOf(Disability.MOBILITY_IMPAIRED, Disability.PHYSICAL_DISABILITY), + description = "ajuda com alimentação e higiene" + ) + + object Car : ServiceType( + provider = Provider.COMMITTEE_MEMBER, + disability = mutableSetOf(Disability.PHYSICAL_DISABILITY, Disability.MOBILITY_IMPAIRED), + description = "transporte para deslocamento no evento" + ) + + companion object { + @JvmStatic + fun getServicesByDisability(disability: Disability): List { + return ServiceType::class.sealedSubclasses + .mapNotNull { it.objectInstance } + .filter { disability in it.disability } + } } } \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index 145ce77..ca2bc50 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -16,7 +16,8 @@ class FirstContactService( private val monitorRepository: MonitorRepository, private val committeeMemberRepository: CommitteeMemberRepository, private val registerPWDService: RegisterPWDService, - private val whatsappService: WhatsappService + private val whatsappService: WhatsappService, + private val attendanceService: AttendanceService ) { val logger: Logger = LoggerFactory.getLogger(WhatsappWebhookController::class.java) fun redirectFluxByUserType(phoneNumber: String, change: Change) { @@ -34,6 +35,7 @@ class FirstContactService( } else { // Completar com o resto da menssagem whatsappService.sendMessage(botNumber, phoneNumber, "Olá, ${pwd.name}.") + attendanceService.sendServices(botNumber,phoneNumber, pwd.disability.first()) } } monitorRepository.findByPhoneNumber(phoneNumber) != null || committeeMemberRepository.findByPhoneNumber(phoneNumber) != null-> { diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt deleted file mode 100644 index 58efb3d..0000000 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt +++ /dev/null @@ -1,24 +0,0 @@ -package ufrpe.sbpc.botpcd.entity - -import jakarta.persistence.Column -import jakarta.persistence.Entity -import jakarta.persistence.GeneratedValue -import jakarta.persistence.GenerationType -import jakarta.persistence.Id -import org.hibernate.annotations.CreationTimestamp -import org.springframework.context.annotation.Profile -import java.time.LocalDateTime - -@Entity -class MessageExchange( - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - var id: Long? = null, - var fromNumber: String, - var toNumber: String, - @Column(columnDefinition = "TEXT") - var message: String -) { - @CreationTimestamp - var createAt: LocalDateTime? = null -} \ No newline at end of file diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt deleted file mode 100644 index b0483b4..0000000 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ufrpe.sbpc.botpcd.repository - -import org.hibernate.annotations.processing.SQL -import org.springframework.context.annotation.Profile -import org.springframework.data.jpa.repository.JpaRepository -import ufrpe.sbpc.botpcd.entity.MessageExchange - - -interface MessageExchangeRepository: JpaRepository { - @SQL("SELECT * FROM MessageExchange ORDER BY createAt ASC LIMIT 1") - fun findLatestMessage(): MessageExchange? -} \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature b/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature index 4db3968..c05bd68 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature @@ -10,14 +10,14 @@ Funcionalidade: Acessar serviços de assistência Exemplos: | tipo_de_deficiência | opccoes_de_servico | - | Surdez | Informações em Libras,atividade com interpretação em Libras | + | Surdez | informações em Libras,atividade com interpretação em Libras | | Mobilidade Reduzida | ajuda na mobilidade,transporte para deslocamento no evento | | Deficiência Física | ajuda na mobilidade,ajuda com alimentação e higiene,transporte para deslocamento no evento | | Cegueira | ajuda na mobilidade,programação com audiodescrição | | Neurodivergente | suporte para pessoas neurodivergentes | | Surdocegueira | guia-intérprete | - Esquema do Cenário Direcionar para o atendente(monitor ou membro da comissão) + Esquema do Cenário Direcionar para o atendente(monitor ou membro da comissão) Dado o bot enviou a mensagem de opção de serviço Quando o PCD selecionar o Entao bot vai direcionar para o disponível no momento diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index d04cf0d..b3ba695 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -5,7 +5,7 @@ Funcionalidade: Cadastro do PCD Cenario: Usuário não cadastrado manda qualquer mensagem Dado usuário não cadastrado Quando usuário mandar qualquer mensagem para o botpcd - Entao bot envia mensagem "com os tipos de deficiencias" + Entao bot envia mensagem "Olá, qual sua deficiência?\n- Digite 1 para Deficiência visual\n- Digite 2 para Deficiência auditiva/surdez\n- Digite 3 para Surdocegueira\n- Digite 4 para Transtorno do Espectro Autista/Neurodivergente\n- Digite 5 para Deficiência física\n- Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida\n- Digite 7 para Não preciso de suporte." Regra: Apenas Usuarios com deficiência podem ser cadastrados no sistema Cenário de Fundo: usuário que recebeu a mensagem "com os tipos de deficiencias" E usuário não cadastrado From 365bfb439e91d46bf564f83e00bee1301485b9d5 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Mon, 2 Jun 2025 22:21:40 -0300 Subject: [PATCH 52/60] WIP improve the tests --- .../kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt | 4 ++-- .../ufrpe/sbpc/botpcd/acessarServicos.feature | 14 ++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt index 576c764..830c035 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt @@ -22,9 +22,9 @@ enum class Disability(val textOption: String) { return disability } fun getOptions(): String { - var message = "Olá, qual sua deficiência?\n" + var message = "Olá, qual sua deficiência?" for (disability in Disability.entries) { - message += "Digite ${disability.ordinal + 1} para ${disability.textOption} \n" + message += "- Digite ${disability.ordinal + 1} para ${disability.textOption} \n" } message += "Digite 7 para Não preciso de suporte." return message diff --git a/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature b/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature index c05bd68..ad24eba 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature @@ -2,8 +2,6 @@ Funcionalidade: Acessar serviços de assistência Como um PCD, quero poder acessar os serviços que estão disponíveis para o meu tipo de deficiência, para que eu consiga aproveitar o evento da SBPC. - Cenário de Fundo: pcd está cadastrado - Esquema do Cenário Listar serviços de acordo com meu tipo de deficiência Quando O PCD com a deficiência de mandar qualquer mensagem Entao O bot vai enviar uma lista de opções de acordo com a @@ -18,8 +16,8 @@ Funcionalidade: Acessar serviços de assistência | Surdocegueira | guia-intérprete | Esquema do Cenário Direcionar para o atendente(monitor ou membro da comissão) - Dado o bot enviou a mensagem de opção de serviço - Quando o PCD selecionar o + Dado bot enviou a mensagem de opção de serviço + Quando PCD envia a mensagem "" Entao bot vai direcionar para o disponível no momento Exemplos: @@ -35,12 +33,12 @@ Funcionalidade: Acessar serviços de assistência Cenário: Bot direciona mensagem do PCD para o atendente correto Dado Atendente aceitou o atendimento do PCD - Quando o PCD envia uma mensagem para o bot + Quando pcd envia qualquer mensagem para o bot Então o Atendente deve receber a mensagem do PCD - E NENHUM outro Atendente ou PCD deve receber a mesma mensagem + E nemhum outro Atendente ou PCD deve receber a mesma mensagem Cenário: Bot direciona mensagem do atendente para o PCD correto Dado que o Atendente aceitou o atendimento do PCD - Quando o Atendente envia uma mensagem para o bot + Quando atendente envia uma mensagem para o bot Então o PCD deve receber a mensagem do Atendente - E NENHUM outro PCD ou Atendente deve receber a mesma mensagem + E nemhum outro PCD ou Atendente deve receber a mesma mensagem From 550ca12b11f1c6ee5f7b859bcb2de9b959831d6f Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 3 Jun 2025 00:07:02 -0300 Subject: [PATCH 53/60] WIP Working in testes cenarios --- .../ufrpe/sbpc/botpcd/entity/Disability.kt | 8 +- .../ufrpe/sbpc/botpcd/PayloadMockFactory.kt | 33 ----- .../sbpc/botpcd/RegisterStepDefinitions.kt | 72 ----------- .../ufrpe/sbpc/botpcd/StepDefinitions.kt | 118 ++++++++++++++++++ .../ufrpe/sbpc/botpcd/registerPCD.feature | 71 ++++++++--- 5 files changed, 174 insertions(+), 128 deletions(-) delete mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/PayloadMockFactory.kt delete mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt create mode 100644 src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt index 830c035..e90a9f4 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/Disability.kt @@ -14,7 +14,7 @@ enum class Disability(val textOption: String) { companion object { - fun parse(text: String): Disability { + fun getByText(text: String): Disability { val disability = Disability.entries.find { it.textOption.equals(text, ignoreCase = true) } if(disability == null) { throw IllegalArgumentException("Invalid disability type: $text") @@ -22,11 +22,11 @@ enum class Disability(val textOption: String) { return disability } fun getOptions(): String { - var message = "Olá, qual sua deficiência?" + var message = "Olá, qual sua deficiência?\n" for (disability in Disability.entries) { - message += "- Digite ${disability.ordinal + 1} para ${disability.textOption} \n" + message += "- Digite ${disability.ordinal + 1} para ${disability.textOption}\n" } - message += "Digite 7 para Não preciso de suporte." + message += "- Digite 7 para Não preciso de suporte." return message } fun getByOrdinal(ordinal: Int) = Disability.entries.find { it.ordinal == ordinal } diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/PayloadMockFactory.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/PayloadMockFactory.kt deleted file mode 100644 index 5c37b01..0000000 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/PayloadMockFactory.kt +++ /dev/null @@ -1,33 +0,0 @@ -package ufrpe.sbpc.botpcd - -import com.whatsapp.api.domain.webhook.Change -import com.whatsapp.api.domain.webhook.Value -import com.whatsapp.api.domain.webhook.WebHook -import org.hibernate.sql.ast.tree.insert.Values -import org.springframework.stereotype.Service -import java.io.File - -@Service -class PayloadMockFactory { - - fun loadPayload(filePath: String): String { - return File(filePath).readText(Charsets.UTF_8) - } - - fun changeUserNumber(payload: String, newNumber: String): String { - return payload.replace(Regex("(?<=\"wa_id\": \")\\d+(?=\")"), newNumber) - } - - fun changeUserMessage(payload: String, newMessage: String): String { - // Exemplo simples substituindo o texto da mensagem. - // Ajuste de acordo com a estrutura do JSON. - return payload.replace(Regex("(?<=\\\"body\\\": \\\").*?(?=\\\")"), newMessage) - } - fun getBotNumber(payload: String): String { - return getChange(payload).metadata.displayPhoneNumber - } - fun getChange(payload: String): Value { - return WebHook.constructEvent(payload).entry[0].changes[0].value - } - fun getMessage(payload: String) = getChange(payload).messages[0].text.body -} \ No newline at end of file diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt deleted file mode 100644 index 266a3f9..0000000 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/RegisterStepDefinitions.kt +++ /dev/null @@ -1,72 +0,0 @@ -package ufrpe.sbpc.botpcd - -import com.fasterxml.jackson.databind.json.JsonMapper -import com.whatsapp.api.domain.webhook.WebHook -import io.cucumber.java.en.Given -import io.cucumber.java.en.Then -import io.cucumber.java.en.When -import org.junit.jupiter.api.Assertions.assertEquals -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post -import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status -import ufrpe.sbpc.botpcd.entity.Disability -import ufrpe.sbpc.botpcd.entity.MessageExchange -import ufrpe.sbpc.botpcd.repository.MessageExchangeRepository -import ufrpe.sbpc.botpcd.repository.PWDRepository -import java.io.File - -class RegisterStepDefinitions( - private val mockMvc: MockMvc, - val whatsappBusinessCloudApiMock: WhatsappBusinessCloudApiMock, - val pwdRepository: PWDRepository, - val payloadMockFactory: PayloadMockFactory, - val messageExchangeRepository: MessageExchangeRepository, -) { - private val numberUserNotRegister: String = "558187654321" - @Given("usuário não cadastrado") - fun `usuário não cadastrado`() { - assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister), null) - } - - @When("usuário mandar qualquer mensagem para o botpcd") - fun `usuário mandar qualquer mensagem para o botpcd`() { - var payload = payloadMockFactory.loadPayload("src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-oi.json") - payload = payloadMockFactory.changeUserNumber(payload, numberUserNotRegister) - mockMvc.perform( - post("/webhooks") - .content(payload) - .contentType("application/json") - ).andExpect(status().isOk) - messageExchangeRepository.save(MessageExchange( - fromNumber = numberUserNotRegister, - toNumber = payloadMockFactory.getBotNumber(payload), - message = payloadMockFactory.getMessage(payload) - )) - } - - @Then("bot envia mensagem {string}") - fun `bot envia mensagem`(message: String) { - var mensagemEsperada = "" - if(message == "com os tipos de deficiencias") { - mensagemEsperada = """Olá, qual sua deficiência? -Digite 1 para Deficiência visual -Digite 2 para Deficiência auditiva/surdez -Digite 3 para Surdocegueira -Digite 4 para Transtorno do Espectro Autista/Neurodivergente -Digite 5 para Deficiência física -Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida -Digite 7 para Não preciso de suporte. - """.trimIndent() - } else { - mensagemEsperada = message - } - val apiMock = whatsappBusinessCloudApiMock - val mensagemEnviada = apiMock.capturedMessage!!.textMessage!!.body - assertEquals(mensagemEnviada, mensagemEsperada) - } - @Given("usuário que recebeu a mensagem {string}") - fun `usuário que recebeu a mensagem`(message: String) { - val latestMessage = messageExchangeRepository.findLatestMessage() - } -} diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt new file mode 100644 index 0000000..3808782 --- /dev/null +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt @@ -0,0 +1,118 @@ +package ufrpe.sbpc.botpcd + +import com.whatsapp.api.domain.webhook.Value +import com.whatsapp.api.domain.webhook.WebHook +import io.cucumber.java.pt.Dado +import io.cucumber.java.pt.Entao +import io.cucumber.java.pt.Quando +import org.junit.jupiter.api.Assertions.assertEquals +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import ufrpe.sbpc.botpcd.entity.MessageExchange +import ufrpe.sbpc.botpcd.repository.MessageExchangeRepository +import ufrpe.sbpc.botpcd.repository.PWDRepository +import java.io.File + +class StepDefinitions( + private val mockMvc: MockMvc, + val whatsappBusinessCloudApiMock: WhatsappBusinessCloudApiMock, + val pwdRepository: PWDRepository, + val messageExchangeRepository: MessageExchangeRepository, +) { + private val numberUserNotRegister: String = "558187654321" + private val botNumber: String = "15556557522" + @Dado("usuário não cadastrado") + fun `usuário não cadastrado`() { + assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister), null) + } + @Quando("usuário envia mensagem {string}") + fun `usuário envia a mensagem`(mensagemEnviada: String) { + userSendMessage(mensagemEnviada, numberUserNotRegister) + } + @Entao("bot envia mensagem {string}") + fun `bot envia mensagem quotes strings`(mensagemEsperada: String) { + testarMensagemEnviada(mensagemEsperada) + } + @Entao("bot registrar o usuário com {string}") + fun `bot registra deficiencia do usuário`(deficiencia: String) { + assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister)!!.disability.first().textOption, deficiencia) + } + @Entao("bot envia mensagem") + fun `bot envia mensagem docs string`(mensagemEsperada: String) { + testarMensagemEnviada(mensagemEsperada) + } + @Dado("usuário recebeu mensagem {string}") + fun `usuário recebeu mensagem`(message: String) { + testarMensagemRecibida(message) + } + @Dado("usuário recebeu mensagem") + fun `usuário recebeu mensagem docs string`(message: String) { + testarMensagemRecibida(message) + } + @Dado("pcd está cadastrado") + fun pcdEstaCadastrado() { + // Implementar lógica para garantir que o PCD está cadastrado no sistema + } + fun testarMensagemEnviada(mensagemEsperada: String) { + val apiMock = whatsappBusinessCloudApiMock + val mensagemEnviada = apiMock.capturedMessage!!.textMessage!!.body + assertEquals(mensagemEnviada, mensagemEsperada) + } + fun testarMensagemRecibida(mensagemRecebida: String) { + val latestMessage = messageExchangeRepository.findFirstByToNumberOrderByCreateAtDesc(this.numberUserNotRegister) + assertEquals(latestMessage!!.message, mensagemRecebida) + } + private fun userSendMessage(mensagemEnviada: String, userPhoneNumber: String) { + val payload = loadPayload("src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-oi.json") + .changeUserNumber(userPhoneNumber) + .changeUserMessage(mensagemEnviada) + .changeBotNumber(botNumber) + + mockMvc.perform( + post("/webhooks") + .content(payload) + .contentType("application/json") + ).andExpect(status().isOk) + + messageExchangeRepository.save( + MessageExchange( + fromNumber = numberUserNotRegister, + toNumber = payload.getBotNumber(), + message = payload.getMessageBody() + ) + ) + } + + @Quando("O PCD com a deficiência de {string} envia mensagem {string}") + fun pcdComDeficienciaMandaMensagem(deficiencia: String, mensagem: String) { + // Implementar lógica para simular a mensagem enviada pelo PCD + } + + @Entao("O bot vai enviar uma lista de opções de acordo com a {string}") + fun botEnviaListaOpcoes(opcoes: String) { + // Implementar lógica para verificar se a resposta do bot corresponde às opções esperadas + } +} + +fun loadPayload(filePath: String): String { + return File(filePath).readText(Charsets.UTF_8) +} +fun String.changeUserNumber(newNumber: String): String { + return this.replace(Regex("(?<=\"wa_id\": \")\\d+(?=\")"), newNumber) +} +fun String.changeUserMessage(newMessage: String): String { + return this.replace(Regex("(?<=\\\"body\\\": \\\").*?(?=\\\")"), newMessage) +} +fun String.changeBotNumber(newNumber: String): String { + return this.replace(Regex("(?<=\\\"display_phone_number\\\": \\\").*?(?=\\\")"), newNumber) +} +fun String.getChange(): Value { + return WebHook.constructEvent(this).entry[0].changes[0].value +} +fun String.getBotNumber(): String { + return this.getChange().metadata.displayPhoneNumber +} +fun String.getMessageBody(): String { + return this.getChange().messages[0].text.body +} \ No newline at end of file diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index b3ba695..c87ed0d 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -4,32 +4,65 @@ Funcionalidade: Cadastro do PCD Cenario: Usuário não cadastrado manda qualquer mensagem Dado usuário não cadastrado - Quando usuário mandar qualquer mensagem para o botpcd - Entao bot envia mensagem "Olá, qual sua deficiência?\n- Digite 1 para Deficiência visual\n- Digite 2 para Deficiência auditiva/surdez\n- Digite 3 para Surdocegueira\n- Digite 4 para Transtorno do Espectro Autista/Neurodivergente\n- Digite 5 para Deficiência física\n- Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida\n- Digite 7 para Não preciso de suporte." + Quando usuário envia mensagem "Oi" + Entao bot envia mensagem + """ +Olá, qual sua deficiência? +- Digite 1 para Deficiência visual +- Digite 2 para Deficiência auditiva/surdez +- Digite 3 para Surdocegueira +- Digite 4 para Transtorno do Espectro Autista/Neurodivergente +- Digite 5 para Deficiência física +- Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida +- Digite 7 para Não preciso de suporte. +""" Regra: Apenas Usuarios com deficiência podem ser cadastrados no sistema - Cenário de Fundo: usuário que recebeu a mensagem "com os tipos de deficiencias" E usuário não cadastrado + Cenário de Fundo: usuário recebeu mensagem + """ +Olá, qual sua deficiência? +- Digite 1 para Deficiência visual +- Digite 2 para Deficiência auditiva/surdez +- Digite 3 para Surdocegueira +- Digite 4 para Transtorno do Espectro Autista/Neurodivergente +- Digite 5 para Deficiência física +- Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida +- Digite 7 para Não preciso de suporte. +""" + E usuário não cadastrado Cenario: O usuário responde que não precisa de suporte - Quando O usuário envia a mensagem "7" - Então bot envia a mensagem "Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC." + Quando usuário envia mensagem "7" + Então bot envia mensagem "Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC." Cenario: - Quando o usuário envia mensagem "10" - Entao bot envia mensagem "com os tipos de deficiencias" + Quando usuário envia mensagem "10" + Entao bot envia mensagem + """ +Olá, qual sua deficiência? +- Digite 1 para Deficiência visual +- Digite 2 para Deficiência auditiva/surdez +- Digite 3 para Surdocegueira +- Digite 4 para Transtorno do Espectro Autista/Neurodivergente +- Digite 5 para Deficiência física +- Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida +- Digite 7 para Não preciso de suporte. +""" Esquema do Cenario: O usuário responde que tem deficiência - Quando usuário envia a mensagem "" - Então o bot vai registrar o tipo de deficiência do usuário E bot vai envia a mensagem "Qual o seu nome?" + Quando usuário envia mensagem "" + Então bot registrar o usuário com "" + E bot envia mensagem "Qual o seu nome?" + Exemplos: - | numero_da_deficiencia | - | 1 | - | 2 | - | 3 | - | 4 | - | 5 | - | 6 | + | numero_da_deficiencia | tipo_deficiencia | + | 1 | Deficiência visual | + | 2 | Deficiência auditiva/surdez | + | 3 | Surdocegueira | + | 4 | Transtorno do Espectro Autista/Neurodivergente | + | 5 | Deficiência física | + | 6 | Não tenho deficiência, mas tenho mobilidade reduzida | Cenario: O usuário responde com o seu nome - Dado usuário recebeu a mensagem do bot "Qual o seu nome?" - Quando usuário mandar qualquer mensagem para o botpcd - Entao bot envia a mensagem "Cadastro realizado" E bot salva o nome do usuário + Dado usuário recebeu mensagem "Qual o seu nome?" + Quando usuário envia mensagem "João Victor" + Entao bot envia a mensagem "Cadastro realizado" E bot salva o nome do usuário From b8858dfa1d49279cf9b104b1f7e6f620fafb570f Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 3 Jun 2025 00:07:35 -0300 Subject: [PATCH 54/60] Create the message repository --- .../sbpc/botpcd/entity/MessageExchange.kt | 23 +++++++++++++++++++ .../repository/MessageExchangeRepository.kt | 10 ++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt new file mode 100644 index 0000000..717fbba --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt @@ -0,0 +1,23 @@ +package ufrpe.sbpc.botpcd.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import org.hibernate.annotations.CreationTimestamp +import java.time.LocalDateTime + +@Entity +class MessageExchange( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, + var fromNumber: String, + var toNumber: String, + @Column(columnDefinition = "TEXT") + var message: String +) { + @CreationTimestamp + var createAt: LocalDateTime? = null +} \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt new file mode 100644 index 0000000..4b53287 --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt @@ -0,0 +1,10 @@ +package ufrpe.sbpc.botpcd.repository + +import org.hibernate.annotations.processing.SQL +import org.springframework.data.jpa.repository.JpaRepository +import ufrpe.sbpc.botpcd.entity.MessageExchange + +interface MessageExchangeRepository: JpaRepository { + fun findFirstByFromNumberOrderByCreateAtDesc(fromNumber: String): MessageExchange? + fun findFirstByToNumberOrderByCreateAtDesc(toNumber: String): MessageExchange? +} \ No newline at end of file From 370830288d269713c0a4067fd407cb987aeb85f3 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 3 Jun 2025 21:40:01 -0300 Subject: [PATCH 55/60] try to fix tests --- .../kotlin/ufrpe/sbpc/botpcd/entity/PWD.kt | 14 ++++-- .../ufrpe/sbpc/botpcd/entity/PWDDisability.kt | 26 ++++++++++ .../botpcd/service/FirstContactService.kt | 13 +++-- .../sbpc/botpcd/service/RegisterPWDService.kt | 8 +++- .../ufrpe/sbpc/botpcd/StepDefinitions.kt | 24 ++++++++-- .../ufrpe/sbpc/botpcd/acessarServicos.feature | 47 ++++++++++++++----- .../ufrpe/sbpc/botpcd/registerPCD.feature | 24 +++++----- 7 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWDDisability.kt diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWD.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWD.kt index 45a6a43..e082ed4 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWD.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWD.kt @@ -1,8 +1,14 @@ package ufrpe.sbpc.botpcd.entity +import jakarta.persistence.CascadeType +import jakarta.persistence.ElementCollection import jakarta.persistence.Entity import jakarta.persistence.EnumType import jakarta.persistence.Enumerated +import jakarta.persistence.CollectionTable +import jakarta.persistence.Column +import jakarta.persistence.JoinColumn +import jakarta.persistence.OneToMany import jakarta.persistence.Table import jakarta.validation.constraints.NotEmpty; import org.springframework.data.repository.NoRepositoryBean @@ -17,7 +23,9 @@ import org.springframework.data.repository.NoRepositoryBean class PWD( name: String? = null, phoneNumber: String, - @NotEmpty(message = "The PWD needs to have a disability") - @Enumerated(value = EnumType.STRING) - var disability: MutableSet + @ElementCollection(targetClass = Disability::class) + @CollectionTable(name = "tb_pwd_disabilities", joinColumns = [JoinColumn(name = "pwd_id")]) + @Column(name = "disability") + @Enumerated(EnumType.STRING) + var disabilities: MutableSet = mutableSetOf() ): User(name = name, phoneNumber = phoneNumber) \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWDDisability.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWDDisability.kt new file mode 100644 index 0000000..c2e888e --- /dev/null +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/PWDDisability.kt @@ -0,0 +1,26 @@ +package ufrpe.sbpc.botpcd.entity + +import jakarta.persistence.Entity +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.JoinColumn +import jakarta.persistence.ManyToOne +import jakarta.persistence.Table + +@Entity +@Table(name="tb_pwd_disability") +class PWDDisability( + @Id + @GeneratedValue(strategy= GenerationType.IDENTITY) + val id: Long?= null, + + @Enumerated(EnumType.STRING) + val disability: Disability, + + @ManyToOne + @JoinColumn(name="pwd_id") + val pwd: PWD +) \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index ca2bc50..ca5d55c 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -6,7 +6,9 @@ import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import ufrpe.sbpc.botpcd.controller.WhatsappWebhookController import ufrpe.sbpc.botpcd.entity.Disability +import ufrpe.sbpc.botpcd.entity.MessageExchange import ufrpe.sbpc.botpcd.repository.CommitteeMemberRepository +import ufrpe.sbpc.botpcd.repository.MessageExchangeRepository import ufrpe.sbpc.botpcd.repository.MonitorRepository import ufrpe.sbpc.botpcd.repository.PWDRepository @@ -17,31 +19,34 @@ class FirstContactService( private val committeeMemberRepository: CommitteeMemberRepository, private val registerPWDService: RegisterPWDService, private val whatsappService: WhatsappService, - private val attendanceService: AttendanceService + private val attendanceService: AttendanceService, + private val messageExchangeRepository: MessageExchangeRepository ) { val logger: Logger = LoggerFactory.getLogger(WhatsappWebhookController::class.java) + fun redirectFluxByUserType(phoneNumber: String, change: Change) { // to get a message from the change change.value.messages[0].text.body val disabilityNumberOptions = Disability.entries.map { it.ordinal.toString() }.toMutableList().apply { this.add("7") } val botNumber = change.value.metadata.phoneNumberId val message = change.value.messages[0].text.body.trim() + val lastBotMessage = messageExchangeRepository.findFirstByFromNumberOrderByCreateAtDesc(botNumber) when { pwdRepository.findByPhoneNumber(phoneNumber) != null -> { val pwd = pwdRepository.findByPhoneNumber(phoneNumber)!! // Nome ainda não registrado - if(pwd.name == null) { + if((lastBotMessage?.message ?: "Qual o seu nome?") == "" && pwd.name == null) { registerPWDService.registerName(pwd, message) whatsappService.sendMessage(botNumber, phoneNumber, "Cadastro realizado.") } else { // Completar com o resto da menssagem whatsappService.sendMessage(botNumber, phoneNumber, "Olá, ${pwd.name}.") - attendanceService.sendServices(botNumber,phoneNumber, pwd.disability.first()) + attendanceService.sendServices(botNumber,phoneNumber, pwd.disabilities.first()) } } monitorRepository.findByPhoneNumber(phoneNumber) != null || committeeMemberRepository.findByPhoneNumber(phoneNumber) != null-> { } - message in disabilityNumberOptions -> { + (lastBotMessage?.message ?: "") == Disability.getOptions() && message in disabilityNumberOptions -> { val disabilityNumber = message.toInt() val ordinalDisability = disabilityNumber - 1 val disability = Disability.getByOrdinal(ordinalDisability) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt index 7815f19..40b1336 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt @@ -3,15 +3,18 @@ package ufrpe.sbpc.botpcd.service import org.springframework.stereotype.Service import ufrpe.sbpc.botpcd.repository.PWDRepository import ufrpe.sbpc.botpcd.entity.Disability +import ufrpe.sbpc.botpcd.entity.MessageExchange import ufrpe.sbpc.botpcd.entity.PWD +import ufrpe.sbpc.botpcd.repository.MessageExchangeRepository @Service class RegisterPWDService( private val pwdRepository: PWDRepository, - private val whatsappService: WhatsappService + private val whatsappService: WhatsappService, + private val messageExchangeRepository: MessageExchangeRepository ) { fun registerDisability(botPhoneNumber: String, pwdPhoneNumber: String, disability: Disability) { - val pwd = PWD(disability = mutableSetOf(disability), phoneNumber = pwdPhoneNumber) + val pwd = PWD(disabilities = mutableSetOf(disability), phoneNumber = pwdPhoneNumber) pwdRepository.save(pwd) } fun registerName(pwd: PWD, name: String) { @@ -20,6 +23,7 @@ class RegisterPWDService( } fun whatIsYourDisability(botPhoneNumber: String, pwdPhoneNumber: String) { whatsappService.sendMessage(botPhoneNumber,pwdPhoneNumber, Disability.getOptions()) + messageExchangeRepository.save(MessageExchange(fromNumber = botPhoneNumber, toNumber = pwdPhoneNumber, message = Disability.getOptions())) } fun whatsIsYourName(botPhoneNumber: String, pwdPhoneNumber: String) { diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt index 3808782..ae3d67f 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt @@ -5,7 +5,9 @@ import com.whatsapp.api.domain.webhook.WebHook import io.cucumber.java.pt.Dado import io.cucumber.java.pt.Entao import io.cucumber.java.pt.Quando +import io.cucumber.spring.ScenarioScope import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status @@ -14,6 +16,7 @@ import ufrpe.sbpc.botpcd.repository.MessageExchangeRepository import ufrpe.sbpc.botpcd.repository.PWDRepository import java.io.File + class StepDefinitions( private val mockMvc: MockMvc, val whatsappBusinessCloudApiMock: WhatsappBusinessCloudApiMock, @@ -24,7 +27,7 @@ class StepDefinitions( private val botNumber: String = "15556557522" @Dado("usuário não cadastrado") fun `usuário não cadastrado`() { - assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister), null) + assertNull(pwdRepository.findByPhoneNumber(numberUserNotRegister)) } @Quando("usuário envia mensagem {string}") fun `usuário envia a mensagem`(mensagemEnviada: String) { @@ -34,9 +37,22 @@ class StepDefinitions( fun `bot envia mensagem quotes strings`(mensagemEsperada: String) { testarMensagemEnviada(mensagemEsperada) } - @Entao("bot registrar o usuário com {string}") + @Entao("bot registra o usuário com deficiencia {string}") fun `bot registra deficiencia do usuário`(deficiencia: String) { - assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister)!!.disability.first().textOption, deficiencia) + assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister)!!.disabilities.first().textOption, deficiencia) + } + @Entao("bot salva o nome do usuário {string}") + fun `bot salva o nome do usuário`(nome: String) { + assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister)!!.name, nome) + } + @Entao("o bot deve registrar a deficiência {string} para o usuário") + fun oBotRegistraDeficienciaUsuario(deficiencia: String) { + val registro = pwdRepository.findByPhoneNumber(numberUserNotRegister) + assertEquals(deficiencia, registro!!.disabilities.first().textOption) + } + @Entao("o bot deve perguntar o nome do usuário") + fun oBotPerguntaNomeUsuario() { + testarMensagemEnviada("Qual o seu nome?") } @Entao("bot envia mensagem") fun `bot envia mensagem docs string`(mensagemEsperada: String) { @@ -57,7 +73,7 @@ class StepDefinitions( fun testarMensagemEnviada(mensagemEsperada: String) { val apiMock = whatsappBusinessCloudApiMock val mensagemEnviada = apiMock.capturedMessage!!.textMessage!!.body - assertEquals(mensagemEnviada, mensagemEsperada) + assertEquals(mensagemEsperada, mensagemEnviada) } fun testarMensagemRecibida(mensagemRecebida: String) { val latestMessage = messageExchangeRepository.findFirstByToNumberOrderByCreateAtDesc(this.numberUserNotRegister) diff --git a/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature b/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature index ad24eba..29cd5cc 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature @@ -3,7 +3,7 @@ Funcionalidade: Acessar serviços de assistência Como um PCD, quero poder acessar os serviços que estão disponíveis para o meu tipo de deficiência, para que eu consiga aproveitar o evento da SBPC. Esquema do Cenário Listar serviços de acordo com meu tipo de deficiência - Quando O PCD com a deficiência de mandar qualquer mensagem + Quando O PCD com a deficiência de "" mandar qualquer mensagem Entao O bot vai enviar uma lista de opções de acordo com a Exemplos: @@ -17,28 +17,51 @@ Funcionalidade: Acessar serviços de assistência Esquema do Cenário Direcionar para o atendente(monitor ou membro da comissão) Dado bot enviou a mensagem de opção de serviço + E existe atendente disponível para o "" Quando PCD envia a mensagem "" Entao bot vai direcionar para o disponível no momento Exemplos: - | servico_desejado | tipo_de_atendente | - | Informações em Libras | monitor | - | 1 Intérprete de Libras | membro da comissão | - | 2 Assistência mobilidade | monitor | - | 3 carro | membro da comissão | - | 4 Higiene e Nutrição | membro da comissão | - | 5 Audiodescrição | membro da comissão | - | 6 Apoio Neurodivergente | monitor | - | 7 Guia-intérprete | membro da comissão | + | servico_desejado | tipo_de_atendente | + | Informações em Libras | monitor | + | Intérprete de Libras | membro da comissão | + | Assistência mobilidade | monitor | + | carro | membro da comissão | + | Higiene e Nutrição | membro da comissão | + | Audiodescrição | membro da comissão | + | Apoio Neurodivergente | monitor | + | Guia-intérprete | membro da comissão | + + Esquema do Cenário: PCD entra na fila de espera + Dado bot enviou a mensagem "" + E não tem atendente disponível + Quando PCD envia mensagem "" + Entao bot envia mensagem "No momento não há atendentes disponíveis. Por favor, aguarde na fila de espera e retornaremos assim que possível." + Exemplos: + | servico_desejado | | + | Informações em Libras | monitor | + | Intérprete de Libras | membro da comissão | + | Assistência mobilidade | monitor | + | carro | membro da comissão | + | Higiene e Nutrição | membro da comissão | + | Audiodescrição | membro da comissão | + | Apoio Neurodivergente | monitor | + | Guia-intérprete | membro da comissão | + Cenário: Bot direciona mensagem do PCD para o atendente correto Dado Atendente aceitou o atendimento do PCD Quando pcd envia qualquer mensagem para o bot - Então o Atendente deve receber a mensagem do PCD + Então o Atendente deve receber a mensagem do PCD contendo o nome do PCD E nemhum outro Atendente ou PCD deve receber a mesma mensagem Cenário: Bot direciona mensagem do atendente para o PCD correto Dado que o Atendente aceitou o atendimento do PCD Quando atendente envia uma mensagem para o bot - Então o PCD deve receber a mensagem do Atendente + Então PCD deve receber a mensagem do Atendente contendo nome do atendente E nemhum outro PCD ou Atendente deve receber a mesma mensagem + + Cenario: Avisar que o atendimento foi encerrado + Dado atendente estava em atendimento com um pcd + Quando atendente encerrou o atendimento + Entao pcd recebeu mensagem "Atendimento Encerrado" diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index c87ed0d..69692f6 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -18,7 +18,8 @@ Olá, qual sua deficiência? """ Regra: Apenas Usuarios com deficiência podem ser cadastrados no sistema - Cenário de Fundo: usuário recebeu mensagem + Cenário de Fundo: Dado usuário não cadastrado + E usuário recebeu mensagem """ Olá, qual sua deficiência? - Digite 1 para Deficiência visual @@ -29,7 +30,6 @@ Olá, qual sua deficiência? - Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida - Digite 7 para Não preciso de suporte. """ - E usuário não cadastrado Cenario: O usuário responde que não precisa de suporte Quando usuário envia mensagem "7" @@ -48,21 +48,21 @@ Olá, qual sua deficiência? - Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida - Digite 7 para Não preciso de suporte. """ - Esquema do Cenario: O usuário responde que tem deficiência Quando usuário envia mensagem "" - Então bot registrar o usuário com "" + Então bot registra o usuário com deficiencia "" E bot envia mensagem "Qual o seu nome?" Exemplos: - | numero_da_deficiencia | tipo_deficiencia | - | 1 | Deficiência visual | - | 2 | Deficiência auditiva/surdez | - | 3 | Surdocegueira | - | 4 | Transtorno do Espectro Autista/Neurodivergente | - | 5 | Deficiência física | - | 6 | Não tenho deficiência, mas tenho mobilidade reduzida | + | numero_da_deficiencia | tipo_deficiencia | + | 1 | Deficiência visual | + | 2 | Deficiência auditiva/surdez | + | 3 | Surdocegueira | + | 4 | Transtorno do Espectro Autista/Neurodivergente | + | 5 | Deficiência física | + | 6 | Não tenho deficiência, mas tenho mobilidade reduzida | Cenario: O usuário responde com o seu nome Dado usuário recebeu mensagem "Qual o seu nome?" Quando usuário envia mensagem "João Victor" - Entao bot envia a mensagem "Cadastro realizado" E bot salva o nome do usuário + Entao bot envia mensagem "Cadastro realizado" + E bot salva o nome do usuário "João Victor" From 7ca3adc5ad3896a207ecfeb0f1068f2649e832fc Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 3 Jun 2025 22:04:30 -0300 Subject: [PATCH 56/60] try to fix tests --- src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt | 6 ++++++ src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt index ae3d67f..e7dfc24 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt @@ -11,7 +11,9 @@ import org.junit.jupiter.api.Assertions.assertNull import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import ufrpe.sbpc.botpcd.entity.Disability import ufrpe.sbpc.botpcd.entity.MessageExchange +import ufrpe.sbpc.botpcd.entity.PWD import ufrpe.sbpc.botpcd.repository.MessageExchangeRepository import ufrpe.sbpc.botpcd.repository.PWDRepository import java.io.File @@ -66,6 +68,10 @@ class StepDefinitions( fun `usuário recebeu mensagem docs string`(message: String) { testarMensagemRecibida(message) } + @Dado("bot enviou mensagem") + fun `bot enviou mensagem`(message: String) { + messageExchangeRepository.save(MessageExchange(fromNumber = botNumber, toNumber = numberUserNotRegister, message = message)) + } @Dado("pcd está cadastrado") fun pcdEstaCadastrado() { // Implementar lógica para garantir que o PCD está cadastrado no sistema diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index 69692f6..db8f099 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -5,7 +5,7 @@ Funcionalidade: Cadastro do PCD Cenario: Usuário não cadastrado manda qualquer mensagem Dado usuário não cadastrado Quando usuário envia mensagem "Oi" - Entao bot envia mensagem + Entao usuário recebeu mensagem """ Olá, qual sua deficiência? - Digite 1 para Deficiência visual @@ -19,7 +19,7 @@ Olá, qual sua deficiência? Regra: Apenas Usuarios com deficiência podem ser cadastrados no sistema Cenário de Fundo: Dado usuário não cadastrado - E usuário recebeu mensagem + E bot enviou mensagem """ Olá, qual sua deficiência? - Digite 1 para Deficiência visual @@ -51,7 +51,7 @@ Olá, qual sua deficiência? Esquema do Cenario: O usuário responde que tem deficiência Quando usuário envia mensagem "" Então bot registra o usuário com deficiencia "" - E bot envia mensagem "Qual o seu nome?" + E usuário recebeu mensagem "Qual o seu nome?" Exemplos: | numero_da_deficiencia | tipo_deficiencia | From 5683e5aca7293936e1ee0a9e97dfd1c51bbb40ec Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 3 Jun 2025 22:06:43 -0300 Subject: [PATCH 57/60] WIP testes --- src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt index e7dfc24..ea84b9f 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt @@ -42,6 +42,7 @@ class StepDefinitions( @Entao("bot registra o usuário com deficiencia {string}") fun `bot registra deficiencia do usuário`(deficiencia: String) { assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister)!!.disabilities.first().textOption, deficiencia) + pwdRepository.delete(pwdRepository.findByPhoneNumber(numberUserNotRegister)!!) } @Entao("bot salva o nome do usuário {string}") fun `bot salva o nome do usuário`(nome: String) { From 18203beda89b5cce9c8b2a5ee93e81d4ee093e4c Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Tue, 3 Jun 2025 22:24:08 -0300 Subject: [PATCH 58/60] WIP tests --- .../sbpc/botpcd/repository/PWDRepository.kt | 3 +++ .../ufrpe/sbpc/botpcd/StepDefinitions.kt | 22 ++++++++++++++----- .../ufrpe/sbpc/botpcd/registerPCD.feature | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/repository/PWDRepository.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/PWDRepository.kt index 1dfcda3..c6a4e55 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/repository/PWDRepository.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/PWDRepository.kt @@ -1,8 +1,11 @@ package ufrpe.sbpc.botpcd.repository import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query import ufrpe.sbpc.botpcd.entity.PWD interface PWDRepository: JpaRepository { fun findByPhoneNumber(phoneNumber: String): PWD? + @Query("SELECT p FROM PWD p JOIN FETCH p.disabilities WHERE p.phoneNumber = :phoneNumber") + fun findByPhoneNumberWithDisabilities(phoneNumber: String): PWD? } \ No newline at end of file diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt index ea84b9f..27c76cd 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt @@ -41,8 +41,9 @@ class StepDefinitions( } @Entao("bot registra o usuário com deficiencia {string}") fun `bot registra deficiencia do usuário`(deficiencia: String) { - assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister)!!.disabilities.first().textOption, deficiencia) - pwdRepository.delete(pwdRepository.findByPhoneNumber(numberUserNotRegister)!!) + val pwd = pwdRepository.findByPhoneNumberWithDisabilities(numberUserNotRegister)!! + assertEquals(pwd.disabilities.first().textOption, deficiencia) + pwdRepository.delete(pwd) } @Entao("bot salva o nome do usuário {string}") fun `bot salva o nome do usuário`(nome: String) { @@ -61,22 +62,33 @@ class StepDefinitions( fun `bot envia mensagem docs string`(mensagemEsperada: String) { testarMensagemEnviada(mensagemEsperada) } + @Entao("usuario recebe mensagem {string}") + fun `usuario recebe mensagem`(message: String) { + testarMensagemRecibida(message) + } + @Entao("usuario recebe mensagem") + fun `usuario recebe mensagem docs string`(message: String) { + testarMensagemRecibida(message) + } @Dado("usuário recebeu mensagem {string}") fun `usuário recebeu mensagem`(message: String) { - testarMensagemRecibida(message) + messageExchangeRepository.save(MessageExchange(fromNumber = botNumber, toNumber = numberUserNotRegister, message = message)) } @Dado("usuário recebeu mensagem") fun `usuário recebeu mensagem docs string`(message: String) { - testarMensagemRecibida(message) + mockUserRecievedMessage(numberUserNotRegister, message) } @Dado("bot enviou mensagem") fun `bot enviou mensagem`(message: String) { - messageExchangeRepository.save(MessageExchange(fromNumber = botNumber, toNumber = numberUserNotRegister, message = message)) + mockUserRecievedMessage(numberUserNotRegister, message) } @Dado("pcd está cadastrado") fun pcdEstaCadastrado() { // Implementar lógica para garantir que o PCD está cadastrado no sistema } + fun mockUserRecievedMessage(userNumber: String, message: String) { + messageExchangeRepository.save(MessageExchange(fromNumber = botNumber, toNumber = numberUserNotRegister, message = message)) + } fun testarMensagemEnviada(mensagemEsperada: String) { val apiMock = whatsappBusinessCloudApiMock val mensagemEnviada = apiMock.capturedMessage!!.textMessage!!.body diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index db8f099..93c6743 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -19,7 +19,7 @@ Olá, qual sua deficiência? Regra: Apenas Usuarios com deficiência podem ser cadastrados no sistema Cenário de Fundo: Dado usuário não cadastrado - E bot enviou mensagem + E usuário recebeu mensagem """ Olá, qual sua deficiência? - Digite 1 para Deficiência visual From b3abcc9aa612a2692721b55a8534c314bb97267f Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Wed, 4 Jun 2025 21:49:05 -0300 Subject: [PATCH 59/60] Fix testes --- .../controller/WhatsappWebhookController.kt | 7 +- .../sbpc/botpcd/entity/MessageExchange.kt | 4 +- .../repository/MessageExchangeRepository.kt | 8 +- .../botpcd/service/FirstContactService.kt | 8 +- .../sbpc/botpcd/service/RegisterPWDService.kt | 1 - .../sbpc/botpcd/service/WhatsappService.kt | 11 +-- .../ufrpe/sbpc/botpcd/StepDefinitions.kt | 92 ++++++------------- .../botpcd/WhatsappBusinessCloudApiMock.kt | 4 +- .../ufrpe/sbpc/botpcd/registerPCD.feature | 31 +++++-- 9 files changed, 74 insertions(+), 92 deletions(-) diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/WhatsappWebhookController.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/WhatsappWebhookController.kt index 98c030a..c9b4152 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/controller/WhatsappWebhookController.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/controller/WhatsappWebhookController.kt @@ -8,11 +8,13 @@ import org.springframework.beans.factory.annotation.Value import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* +import ufrpe.sbpc.botpcd.entity.MessageExchange +import ufrpe.sbpc.botpcd.repository.MessageExchangeRepository import ufrpe.sbpc.botpcd.service.FirstContactService @RestController -class WhatsappWebhookController(private val firstContactService: FirstContactService) { +class WhatsappWebhookController(private val firstContactService: FirstContactService, private val messageExchangeRepository: MessageExchangeRepository) { @Value("\${whatsapp.verify.token}") lateinit var VERIFY_TOKEN: String val logger: Logger = LoggerFactory.getLogger(WhatsappWebhookController::class.java) @@ -31,7 +33,8 @@ class WhatsappWebhookController(private val firstContactService: FirstContactSer if(change.value?.messages == null) { return ResponseEntity.ok("We don't handle this type of message") } - firstContactService.redirectFluxByUserType(change.value.contacts[0].waId, change) + val userPhoneNumber = change.value.contacts[0].waId + firstContactService.redirectFluxByUserType(userPhoneNumber, change) } } // Opcional: validar assinatura com 'signature' diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt index 717fbba..a858c05 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/entity/MessageExchange.kt @@ -13,8 +13,8 @@ class MessageExchange( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) var id: Long? = null, - var fromNumber: String, - var toNumber: String, + var fromPhoneNumber: String, + var toPhoneNumber: String, @Column(columnDefinition = "TEXT") var message: String ) { diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt index 4b53287..a84eba6 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/repository/MessageExchangeRepository.kt @@ -1,10 +1,12 @@ package ufrpe.sbpc.botpcd.repository -import org.hibernate.annotations.processing.SQL import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query import ufrpe.sbpc.botpcd.entity.MessageExchange interface MessageExchangeRepository: JpaRepository { - fun findFirstByFromNumberOrderByCreateAtDesc(fromNumber: String): MessageExchange? - fun findFirstByToNumberOrderByCreateAtDesc(toNumber: String): MessageExchange? + @Query( + "SELECT m FROM MessageExchange m WHERE m.toPhoneNumber = :toPhoneNumber AND m.fromPhoneNumber = :fromPhoneNumber ORDER BY m.createAt DESC LIMIT 1" + ) + fun lastExchangeMessage(toPhoneNumber: String, fromPhoneNumber: String): MessageExchange? } \ No newline at end of file diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt index ca5d55c..79d2f1d 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/FirstContactService.kt @@ -25,16 +25,16 @@ class FirstContactService( val logger: Logger = LoggerFactory.getLogger(WhatsappWebhookController::class.java) fun redirectFluxByUserType(phoneNumber: String, change: Change) { - // to get a message from the change change.value.messages[0].text.body - val disabilityNumberOptions = Disability.entries.map { it.ordinal.toString() }.toMutableList().apply { this.add("7") } + val disabilityNumberOptions = Disability.entries.map { (it.ordinal + 1).toString() }.toMutableList().apply { this.add("7") } val botNumber = change.value.metadata.phoneNumberId val message = change.value.messages[0].text.body.trim() - val lastBotMessage = messageExchangeRepository.findFirstByFromNumberOrderByCreateAtDesc(botNumber) + messageExchangeRepository.save(MessageExchange(fromPhoneNumber = phoneNumber, toPhoneNumber = botNumber, message = message)) + val lastBotMessage = messageExchangeRepository.lastExchangeMessage(fromPhoneNumber = botNumber, toPhoneNumber = phoneNumber) when { pwdRepository.findByPhoneNumber(phoneNumber) != null -> { val pwd = pwdRepository.findByPhoneNumber(phoneNumber)!! // Nome ainda não registrado - if((lastBotMessage?.message ?: "Qual o seu nome?") == "" && pwd.name == null) { + if((lastBotMessage?.message ?: "") == "Qual o seu nome?" && pwd.name == null) { registerPWDService.registerName(pwd, message) whatsappService.sendMessage(botNumber, phoneNumber, "Cadastro realizado.") } else { diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt index 40b1336..316c2df 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/RegisterPWDService.kt @@ -23,7 +23,6 @@ class RegisterPWDService( } fun whatIsYourDisability(botPhoneNumber: String, pwdPhoneNumber: String) { whatsappService.sendMessage(botPhoneNumber,pwdPhoneNumber, Disability.getOptions()) - messageExchangeRepository.save(MessageExchange(fromNumber = botPhoneNumber, toNumber = pwdPhoneNumber, message = Disability.getOptions())) } fun whatsIsYourName(botPhoneNumber: String, pwdPhoneNumber: String) { diff --git a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt index 388b0cf..5d709f9 100644 --- a/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt +++ b/src/main/kotlin/ufrpe/sbpc/botpcd/service/WhatsappService.kt @@ -1,25 +1,24 @@ package ufrpe.sbpc.botpcd.service -import com.whatsapp.api.domain.messages.Action -import com.whatsapp.api.domain.messages.Body -import com.whatsapp.api.domain.messages.InteractiveMessage import com.whatsapp.api.domain.messages.Message -import com.whatsapp.api.domain.messages.Section import com.whatsapp.api.domain.messages.TextMessage -import com.whatsapp.api.domain.messages.type.InteractiveMessageType import com.whatsapp.api.impl.WhatsappBusinessCloudApi import org.springframework.stereotype.Service +import ufrpe.sbpc.botpcd.entity.MessageExchange +import ufrpe.sbpc.botpcd.repository.MessageExchangeRepository @Service class WhatsappService( - private val cloudApi: WhatsappBusinessCloudApi + private val cloudApi: WhatsappBusinessCloudApi, + private val messageExchangeRepository: MessageExchangeRepository ) { fun sendMessage(botNumber: String, destinyNumberID: String, msg: String) { val message = Message.MessageBuilder.builder() .setTo(destinyNumberID) .buildTextMessage(TextMessage().setBody(msg)) cloudApi.sendMessage(botNumber, message) + messageExchangeRepository.save(MessageExchange(fromPhoneNumber = botNumber, toPhoneNumber = destinyNumberID, message = msg)) } fun createOptions(options: List, header: String = ""): String { diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt index 27c76cd..116f7f9 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/StepDefinitions.kt @@ -5,7 +5,6 @@ import com.whatsapp.api.domain.webhook.WebHook import io.cucumber.java.pt.Dado import io.cucumber.java.pt.Entao import io.cucumber.java.pt.Quando -import io.cucumber.spring.ScenarioScope import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNull import org.springframework.test.web.servlet.MockMvc @@ -21,102 +20,69 @@ import java.io.File class StepDefinitions( private val mockMvc: MockMvc, - val whatsappBusinessCloudApiMock: WhatsappBusinessCloudApiMock, val pwdRepository: PWDRepository, val messageExchangeRepository: MessageExchangeRepository, ) { private val numberUserNotRegister: String = "558187654321" private val botNumber: String = "15556557522" + @Dado("usuário recebeu mensagem {string}") + fun `usuário recebeu mensagem`(message: String) { + mockUserRecievedMessage(numberUserNotRegister, message) + } + @Dado("usuário recebeu mensagem") + fun `usuário recebeu mensagem docs string`(message: String) { + mockUserRecievedMessage(numberUserNotRegister, message) + } + @Dado("pcd está cadastrado completo") + fun pcdEstaCadastrado() { + // Implementar lógica para garantir que o PCD está cadastrado no sistema + } @Dado("usuário não cadastrado") fun `usuário não cadastrado`() { assertNull(pwdRepository.findByPhoneNumber(numberUserNotRegister)) } + @Dado("usuário possui deficiência cadastrada") + fun `usuário que possui deficiencia cadastrada`() { + pwdRepository.save(PWD(phoneNumber = numberUserNotRegister, disabilities = mutableSetOf(Disability.BLINDED))) + } @Quando("usuário envia mensagem {string}") - fun `usuário envia a mensagem`(mensagemEnviada: String) { + fun `usuario envia mensagem`(mensagemEnviada: String) { userSendMessage(mensagemEnviada, numberUserNotRegister) } - @Entao("bot envia mensagem {string}") - fun `bot envia mensagem quotes strings`(mensagemEsperada: String) { - testarMensagemEnviada(mensagemEsperada) - } - @Entao("bot registra o usuário com deficiencia {string}") + @Entao("bot registrará o usuário com deficiencia {string}") fun `bot registra deficiencia do usuário`(deficiencia: String) { val pwd = pwdRepository.findByPhoneNumberWithDisabilities(numberUserNotRegister)!! assertEquals(pwd.disabilities.first().textOption, deficiencia) pwdRepository.delete(pwd) } - @Entao("bot salva o nome do usuário {string}") + @Entao("bot salvará o nome do usuário {string}") fun `bot salva o nome do usuário`(nome: String) { assertEquals(pwdRepository.findByPhoneNumber(numberUserNotRegister)!!.name, nome) } - @Entao("o bot deve registrar a deficiência {string} para o usuário") - fun oBotRegistraDeficienciaUsuario(deficiencia: String) { - val registro = pwdRepository.findByPhoneNumber(numberUserNotRegister) - assertEquals(deficiencia, registro!!.disabilities.first().textOption) - } - @Entao("o bot deve perguntar o nome do usuário") - fun oBotPerguntaNomeUsuario() { - testarMensagemEnviada("Qual o seu nome?") - } - @Entao("bot envia mensagem") - fun `bot envia mensagem docs string`(mensagemEsperada: String) { - testarMensagemEnviada(mensagemEsperada) - } - @Entao("usuario recebe mensagem {string}") - fun `usuario recebe mensagem`(message: String) { - testarMensagemRecibida(message) + @Entao("usuário receberá mensagem {string}") + fun `usuario receberá mensagem`(message: String) { + testarMensagemRecebidaDoUsuario(message, numberUserNotRegister) } - @Entao("usuario recebe mensagem") - fun `usuario recebe mensagem docs string`(message: String) { - testarMensagemRecibida(message) - } - @Dado("usuário recebeu mensagem {string}") - fun `usuário recebeu mensagem`(message: String) { - messageExchangeRepository.save(MessageExchange(fromNumber = botNumber, toNumber = numberUserNotRegister, message = message)) - } - @Dado("usuário recebeu mensagem") - fun `usuário recebeu mensagem docs string`(message: String) { - mockUserRecievedMessage(numberUserNotRegister, message) - } - @Dado("bot enviou mensagem") - fun `bot enviou mensagem`(message: String) { - mockUserRecievedMessage(numberUserNotRegister, message) - } - @Dado("pcd está cadastrado") - fun pcdEstaCadastrado() { - // Implementar lógica para garantir que o PCD está cadastrado no sistema + @Entao("usuário receberá mensagem") + fun `usuario receberá mensagem docs string`(message: String) { + testarMensagemRecebidaDoUsuario(message, numberUserNotRegister) } fun mockUserRecievedMessage(userNumber: String, message: String) { - messageExchangeRepository.save(MessageExchange(fromNumber = botNumber, toNumber = numberUserNotRegister, message = message)) + messageExchangeRepository.save(MessageExchange(fromPhoneNumber = botNumber, toPhoneNumber = numberUserNotRegister, message = message)) } - fun testarMensagemEnviada(mensagemEsperada: String) { - val apiMock = whatsappBusinessCloudApiMock - val mensagemEnviada = apiMock.capturedMessage!!.textMessage!!.body - assertEquals(mensagemEsperada, mensagemEnviada) - } - fun testarMensagemRecibida(mensagemRecebida: String) { - val latestMessage = messageExchangeRepository.findFirstByToNumberOrderByCreateAtDesc(this.numberUserNotRegister) - assertEquals(latestMessage!!.message, mensagemRecebida) + fun testarMensagemRecebidaDoUsuario(mensagemEsperada: String, userPhoneNumber: String) { + assertEquals(mensagemEsperada, messageExchangeRepository.lastExchangeMessage(toPhoneNumber = userPhoneNumber, fromPhoneNumber = botNumber)?.message) } private fun userSendMessage(mensagemEnviada: String, userPhoneNumber: String) { val payload = loadPayload("src/test/resources/ufrpe/sbpc/botpcd/mocks/usuario-manda-oi.json") .changeUserNumber(userPhoneNumber) .changeUserMessage(mensagemEnviada) .changeBotNumber(botNumber) - mockMvc.perform( post("/webhooks") .content(payload) .contentType("application/json") ).andExpect(status().isOk) - - messageExchangeRepository.save( - MessageExchange( - fromNumber = numberUserNotRegister, - toNumber = payload.getBotNumber(), - message = payload.getMessageBody() - ) - ) } @Quando("O PCD com a deficiência de {string} envia mensagem {string}") @@ -140,7 +106,7 @@ fun String.changeUserMessage(newMessage: String): String { return this.replace(Regex("(?<=\\\"body\\\": \\\").*?(?=\\\")"), newMessage) } fun String.changeBotNumber(newNumber: String): String { - return this.replace(Regex("(?<=\\\"display_phone_number\\\": \\\").*?(?=\\\")"), newNumber) + return this.replace(Regex("(?<=\\\"phone_number_id\\\": \\\").*?(?=\\\")"), newNumber) } fun String.getChange(): Value { return WebHook.constructEvent(this).entry[0].changes[0].value diff --git a/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt b/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt index 0a5a6bc..825bdc6 100644 --- a/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt +++ b/src/test/kotlin/ufrpe/sbpc/botpcd/WhatsappBusinessCloudApiMock.kt @@ -21,8 +21,8 @@ class WhatsappBusinessCloudApiMock(token: String?) : WhatsappBusinessCloudApi(to // Retorna uma resposta simulada messageExchangeRepository.save( MessageExchange( - fromNumber = phoneNumberId!!, - toNumber = message!!.to, + fromPhoneNumber = phoneNumberId!!, + toPhoneNumber = message!!.to, message = message.textMessage.body ) ) diff --git a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature index 93c6743..531cd70 100644 --- a/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature +++ b/src/test/resources/ufrpe/sbpc/botpcd/registerPCD.feature @@ -5,7 +5,7 @@ Funcionalidade: Cadastro do PCD Cenario: Usuário não cadastrado manda qualquer mensagem Dado usuário não cadastrado Quando usuário envia mensagem "Oi" - Entao usuário recebeu mensagem + Entao usuário receberá mensagem """ Olá, qual sua deficiência? - Digite 1 para Deficiência visual @@ -33,11 +33,11 @@ Olá, qual sua deficiência? Cenario: O usuário responde que não precisa de suporte Quando usuário envia mensagem "7" - Então bot envia mensagem "Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC." + Então usuário receberá mensagem "Agradecemos o contato! Este canal é exclusivo para atendimento de pessoas com deficiência ou mobilidade reduzida que participarão do evento. Desejamos a você uma excelente participação na 77ª Reunião Anual da SBPC." Cenario: Quando usuário envia mensagem "10" - Entao bot envia mensagem + Entao usuário receberá mensagem """ Olá, qual sua deficiência? - Digite 1 para Deficiência visual @@ -49,9 +49,20 @@ Olá, qual sua deficiência? - Digite 7 para Não preciso de suporte. """ Esquema do Cenario: O usuário responde que tem deficiência + Dado usuário recebeu mensagem + """ +Olá, qual sua deficiência? +- Digite 1 para Deficiência visual +- Digite 2 para Deficiência auditiva/surdez +- Digite 3 para Surdocegueira +- Digite 4 para Transtorno do Espectro Autista/Neurodivergente +- Digite 5 para Deficiência física +- Digite 6 para Não tenho deficiência, mas tenho mobilidade reduzida +- Digite 7 para Não preciso de suporte. +""" Quando usuário envia mensagem "" - Então bot registra o usuário com deficiencia "" - E usuário recebeu mensagem "Qual o seu nome?" + Então bot registrará o usuário com deficiencia "" + E usuário receberá mensagem "Qual o seu nome?" Exemplos: | numero_da_deficiencia | tipo_deficiencia | @@ -61,8 +72,10 @@ Olá, qual sua deficiência? | 4 | Transtorno do Espectro Autista/Neurodivergente | | 5 | Deficiência física | | 6 | Não tenho deficiência, mas tenho mobilidade reduzida | - Cenario: O usuário responde com o seu nome + + Cenario: O usuário responde com o seu nome Dado usuário recebeu mensagem "Qual o seu nome?" - Quando usuário envia mensagem "João Victor" - Entao bot envia mensagem "Cadastro realizado" - E bot salva o nome do usuário "João Victor" + E usuário possui deficiência cadastrada + Quando usuário envia mensagem "João Victor" + Entao usuário receberá mensagem "Cadastro realizado." + E bot salvará o nome do usuário "João Victor" From 73d30c2ef42cbce1e8136e342a480202bf3b1185 Mon Sep 17 00:00:00 2001 From: victor-yghor Date: Wed, 4 Jun 2025 21:55:27 -0300 Subject: [PATCH 60/60] remove the tests --- .../ufrpe/sbpc/botpcd/acessarServicos.feature | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature diff --git a/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature b/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature deleted file mode 100644 index 29cd5cc..0000000 --- a/src/test/resources/ufrpe/sbpc/botpcd/acessarServicos.feature +++ /dev/null @@ -1,67 +0,0 @@ -# language: pt -Funcionalidade: Acessar serviços de assistência - Como um PCD, quero poder acessar os serviços que estão disponíveis para o meu tipo de deficiência, para que eu consiga aproveitar o evento da SBPC. - - Esquema do Cenário Listar serviços de acordo com meu tipo de deficiência - Quando O PCD com a deficiência de "" mandar qualquer mensagem - Entao O bot vai enviar uma lista de opções de acordo com a - - Exemplos: - | tipo_de_deficiência | opccoes_de_servico | - | Surdez | informações em Libras,atividade com interpretação em Libras | - | Mobilidade Reduzida | ajuda na mobilidade,transporte para deslocamento no evento | - | Deficiência Física | ajuda na mobilidade,ajuda com alimentação e higiene,transporte para deslocamento no evento | - | Cegueira | ajuda na mobilidade,programação com audiodescrição | - | Neurodivergente | suporte para pessoas neurodivergentes | - | Surdocegueira | guia-intérprete | - - Esquema do Cenário Direcionar para o atendente(monitor ou membro da comissão) - Dado bot enviou a mensagem de opção de serviço - E existe atendente disponível para o "" - Quando PCD envia a mensagem "" - Entao bot vai direcionar para o disponível no momento - - Exemplos: - | servico_desejado | tipo_de_atendente | - | Informações em Libras | monitor | - | Intérprete de Libras | membro da comissão | - | Assistência mobilidade | monitor | - | carro | membro da comissão | - | Higiene e Nutrição | membro da comissão | - | Audiodescrição | membro da comissão | - | Apoio Neurodivergente | monitor | - | Guia-intérprete | membro da comissão | - - Esquema do Cenário: PCD entra na fila de espera - Dado bot enviou a mensagem "" - E não tem atendente disponível - Quando PCD envia mensagem "" - Entao bot envia mensagem "No momento não há atendentes disponíveis. Por favor, aguarde na fila de espera e retornaremos assim que possível." - Exemplos: - | servico_desejado | | - | Informações em Libras | monitor | - | Intérprete de Libras | membro da comissão | - | Assistência mobilidade | monitor | - | carro | membro da comissão | - | Higiene e Nutrição | membro da comissão | - | Audiodescrição | membro da comissão | - | Apoio Neurodivergente | monitor | - | Guia-intérprete | membro da comissão | - - - Cenário: Bot direciona mensagem do PCD para o atendente correto - Dado Atendente aceitou o atendimento do PCD - Quando pcd envia qualquer mensagem para o bot - Então o Atendente deve receber a mensagem do PCD contendo o nome do PCD - E nemhum outro Atendente ou PCD deve receber a mesma mensagem - - Cenário: Bot direciona mensagem do atendente para o PCD correto - Dado que o Atendente aceitou o atendimento do PCD - Quando atendente envia uma mensagem para o bot - Então PCD deve receber a mensagem do Atendente contendo nome do atendente - E nemhum outro PCD ou Atendente deve receber a mesma mensagem - - Cenario: Avisar que o atendimento foi encerrado - Dado atendente estava em atendimento com um pcd - Quando atendente encerrou o atendimento - Entao pcd recebeu mensagem "Atendimento Encerrado"