You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ChannelStateHandler.kt 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package com.dmdirc.ktirc.handlers
  2. import com.dmdirc.ktirc.IrcClient
  3. import com.dmdirc.ktirc.events.*
  4. import com.dmdirc.ktirc.model.ChannelState
  5. import com.dmdirc.ktirc.model.ChannelUser
  6. import com.dmdirc.ktirc.util.logger
  7. internal class ChannelStateHandler : EventHandler {
  8. private val log by logger()
  9. override fun processEvent(client: IrcClient, event: IrcEvent): List<IrcEvent> {
  10. when (event) {
  11. is ChannelJoined -> handleJoin(client, event)
  12. is ChannelParted -> handlePart(client, event)
  13. is ChannelNamesReceived -> handleNamesReceived(client, event)
  14. is ChannelNamesFinished -> handleNamesFinished(client, event)
  15. is ChannelUserKicked -> handleKick(client, event)
  16. is ModeChanged -> handleModeChanged(client, event)
  17. is UserQuit -> return handleQuit(client, event)
  18. is UserNickChanged -> return handleNickChanged(client, event)
  19. }
  20. return emptyList()
  21. }
  22. private fun handleJoin(client: IrcClient, event: ChannelJoined) {
  23. if (client.isLocalUser(event.user)) {
  24. log.info { "Joined new channel: ${event.channel}" }
  25. client.channelState += ChannelState(event.channel) { client.caseMapping }
  26. }
  27. client.channelState[event.channel]?.let { it.users += ChannelUser(event.user.nickname) }
  28. }
  29. private fun handlePart(client: IrcClient, event: ChannelParted) {
  30. if (client.isLocalUser(event.user)) {
  31. log.info { "Left channel: ${event.channel}" }
  32. client.channelState -= event.channel
  33. } else {
  34. client.channelState[event.channel]?.let {
  35. it.users -= event.user.nickname
  36. }
  37. }
  38. }
  39. private fun handleKick(client: IrcClient, event: ChannelUserKicked) {
  40. if (client.isLocalUser(event.victim)) {
  41. log.info { "Kicked from channel: ${event.channel}" }
  42. client.channelState -= event.channel
  43. } else {
  44. client.channelState[event.channel]?.let {
  45. it.users -= event.victim
  46. }
  47. }
  48. }
  49. private fun handleNamesReceived(client: IrcClient, event: ChannelNamesReceived) {
  50. val channel = client.channelState[event.channel] ?: return
  51. if (!channel.receivingUserList) {
  52. log.finer { "Started receiving names list for ${channel.name}" }
  53. channel.users.clear()
  54. channel.receivingUserList = true
  55. }
  56. event.toModesAndUsers(client).forEach { (modes, user) ->
  57. channel.users += ChannelUser(user.nickname, modes)
  58. }
  59. }
  60. private fun handleNamesFinished(client: IrcClient, event: ChannelNamesFinished) {
  61. client.channelState[event.channel]?.let {
  62. it.receivingUserList = false
  63. log.finest { "Finished receiving names in ${event.channel}. Users: ${it.users.toList()}" }
  64. }
  65. }
  66. private fun handleModeChanged(client: IrcClient, event: ModeChanged) {
  67. val chan = client.channelState[event.target] ?: return
  68. if (event.discovered) {
  69. chan.modesDiscovered = true
  70. chan.modes.clear()
  71. }
  72. var adding = true
  73. var argumentOffset = 0
  74. for (char in event.modes) {
  75. when (char) {
  76. '+' -> adding = true
  77. '-' -> adding = false
  78. else -> argumentOffset += if (client.serverState.isChannelUserMode(char)) {
  79. adjustUserMode(client, chan, char, adding, event.arguments[argumentOffset])
  80. } else {
  81. adjustMode(client, chan, char, event.arguments, argumentOffset, adding)
  82. }
  83. }
  84. }
  85. }
  86. private fun adjustMode(client: IrcClient, chan: ChannelState, mode: Char, arguments: Array<String>, argumentOffset: Int, adding: Boolean): Int {
  87. val type = client.serverState.channelModeType(mode)
  88. val takesParam = if (adding) type.needsParameterToSet else type.needsParameterToUnset
  89. val param = if (takesParam) arguments[argumentOffset] else ""
  90. if (adding) {
  91. chan.modes[mode] = param
  92. } else {
  93. chan.modes.remove(mode)
  94. }
  95. return if (takesParam) 1 else 0
  96. }
  97. private fun adjustUserMode(client: IrcClient, chan: ChannelState, mode: Char, adding: Boolean, user: String): Int {
  98. chan.users[user]?.let { channelUser ->
  99. // Filter from the master list of mode prefixes so that ordering is consistent
  100. channelUser.modes = client.serverState.channelModePrefixes.modes.filter {
  101. if (adding) {
  102. it == mode || it in channelUser.modes
  103. } else {
  104. it != mode && it in channelUser.modes
  105. }
  106. }
  107. }
  108. return 1
  109. }
  110. private fun handleQuit(client: IrcClient, event: UserQuit) = sequence {
  111. client.channelState.forEach {
  112. if (it.users.contains(event.user.nickname)) {
  113. it.users -= event.user.nickname
  114. yield(ChannelQuit(event.time, event.user, it.name, event.reason))
  115. }
  116. }
  117. }.toList()
  118. private fun handleNickChanged(client: IrcClient, event: UserNickChanged) = sequence {
  119. client.channelState.forEach {
  120. it.users[event.user.nickname]?.let { chanUser ->
  121. chanUser.nickname = event.newNick
  122. yield(ChannelNickChanged(event.time, event.user, it.name, event.newNick))
  123. }
  124. }
  125. }.toList()
  126. }