channel.ts 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import { fixBlockTimestamp } from '../eventFix'
  2. import { SubstrateEvent } from '@dzlzv/hydra-common'
  3. import { DatabaseManager } from '@dzlzv/hydra-db-utils'
  4. import ISO6391 from 'iso-639-1'
  5. import { FindConditions, In } from 'typeorm'
  6. import { AccountId } from '@polkadot/types/interfaces'
  7. import { Option } from '@polkadot/types/codec'
  8. import { Content } from '../../../generated/types'
  9. import {
  10. readProtobuf,
  11. readProtobufWithAssets,
  12. convertContentActorToChannelOwner,
  13. convertContentActorToDataObjectOwner,
  14. } from './utils'
  15. import { Channel, ChannelCategory, DataObject, AssetAvailability } from 'query-node'
  16. import { inconsistentState, logger } from '../common'
  17. // eslint-disable-next-line @typescript-eslint/naming-convention
  18. export async function content_ChannelCreated(db: DatabaseManager, event: SubstrateEvent): Promise<void> {
  19. // read event data
  20. const { channelId, channelCreationParameters, contentActor } = new Content.ChannelCreatedEvent(event).data
  21. // read metadata
  22. const protobufContent = await readProtobufWithAssets(new Channel(), {
  23. metadata: channelCreationParameters.meta,
  24. db,
  25. event,
  26. assets: channelCreationParameters.assets,
  27. contentOwner: convertContentActorToDataObjectOwner(contentActor, channelId.toNumber()),
  28. })
  29. // create entity
  30. const channel = new Channel({
  31. // main data
  32. id: channelId.toString(),
  33. isCensored: false,
  34. videos: [],
  35. createdInBlock: event.blockNumber,
  36. // default values for properties that might or might not be filled by metadata
  37. coverPhotoUrls: [],
  38. coverPhotoAvailability: AssetAvailability.INVALID,
  39. avatarPhotoUrls: [],
  40. avatarPhotoAvailability: AssetAvailability.INVALID,
  41. // fill in auto-generated fields
  42. createdAt: new Date(fixBlockTimestamp(event.blockTimestamp).toNumber()),
  43. updatedAt: new Date(fixBlockTimestamp(event.blockTimestamp).toNumber()),
  44. // prepare channel owner (handles fields `ownerMember` and `ownerCuratorGroup`)
  45. ...(await convertContentActorToChannelOwner(db, contentActor)),
  46. // integrate metadata
  47. ...protobufContent,
  48. })
  49. // save entity
  50. await db.save<Channel>(channel)
  51. // emit log event
  52. logger.info('Channel has been created', { id: channel.id })
  53. }
  54. // eslint-disable-next-line @typescript-eslint/naming-convention
  55. export async function content_ChannelUpdated(db: DatabaseManager, event: SubstrateEvent) {
  56. // read event data
  57. const { channelId, channelUpdateParameters, contentActor } = new Content.ChannelUpdatedEvent(event).data
  58. // load channel
  59. const channel = await db.get(Channel, { where: { id: channelId.toString() } as FindConditions<Channel> })
  60. // ensure channel exists
  61. if (!channel) {
  62. return inconsistentState('Non-existing channel update requested', channelId)
  63. }
  64. // prepare changed metadata
  65. const newMetadata = channelUpdateParameters.new_meta.unwrapOr(null)
  66. // update metadata if it was changed
  67. if (newMetadata) {
  68. const protobufContent = await readProtobufWithAssets(new Channel(), {
  69. metadata: newMetadata,
  70. db,
  71. event,
  72. assets: channelUpdateParameters.assets.unwrapOr([]),
  73. contentOwner: convertContentActorToDataObjectOwner(contentActor, channelId.toNumber()),
  74. })
  75. // update all fields read from protobuf
  76. for (const [key, value] of Object.entries(protobufContent)) {
  77. channel[key] = value
  78. }
  79. }
  80. // prepare changed reward account
  81. const newRewardAccount = channelUpdateParameters.reward_account.unwrapOr(null)
  82. // reward account change happened?
  83. if (newRewardAccount) {
  84. // this will change the `channel`!
  85. handleChannelRewardAccountChange(channel, newRewardAccount)
  86. }
  87. // set last update time
  88. channel.updatedAt = new Date(fixBlockTimestamp(event.blockTimestamp).toNumber())
  89. // save channel
  90. await db.save<Channel>(channel)
  91. // emit log event
  92. logger.info('Channel has been updated', { id: channel.id })
  93. }
  94. export async function content_ChannelAssetsRemoved(db: DatabaseManager, event: SubstrateEvent) {
  95. // read event data
  96. const { contentId: contentIds } = new Content.ChannelAssetsRemovedEvent(event).data
  97. // load channel
  98. const assets = await db.getMany(DataObject, {
  99. where: {
  100. id: In(contentIds.toArray().map((item) => item.toString())),
  101. } as FindConditions<DataObject>,
  102. })
  103. // delete assets
  104. for (const asset of assets) {
  105. await db.remove<DataObject>(asset)
  106. }
  107. // emit log event
  108. logger.info('Channel assets have been removed', { ids: contentIds })
  109. }
  110. // eslint-disable-next-line @typescript-eslint/naming-convention
  111. export async function content_ChannelCensorshipStatusUpdated(db: DatabaseManager, event: SubstrateEvent) {
  112. // read event data
  113. const { channelId, isCensored } = new Content.ChannelCensorshipStatusUpdatedEvent(event).data
  114. // load event
  115. const channel = await db.get(Channel, { where: { id: channelId.toString() } as FindConditions<Channel> })
  116. // ensure channel exists
  117. if (!channel) {
  118. return inconsistentState('Non-existing channel censoring requested', channelId)
  119. }
  120. // update channel
  121. channel.isCensored = isCensored.isTrue
  122. // set last update time
  123. channel.updatedAt = new Date(fixBlockTimestamp(event.blockTimestamp).toNumber())
  124. // save channel
  125. await db.save<Channel>(channel)
  126. // emit log event
  127. logger.info('Channel censorship status has been updated', { id: channelId, isCensored: isCensored.isTrue })
  128. }
  129. /// ///////////////// ChannelCategory ////////////////////////////////////////////
  130. // eslint-disable-next-line @typescript-eslint/naming-convention
  131. export async function content_ChannelCategoryCreated(db: DatabaseManager, event: SubstrateEvent) {
  132. // read event data
  133. const { channelCategoryCreationParameters, channelCategoryId } = new Content.ChannelCategoryCreatedEvent(event).data
  134. // read metadata
  135. const protobufContent = await readProtobuf(new ChannelCategory(), {
  136. metadata: channelCategoryCreationParameters.meta,
  137. db,
  138. event,
  139. })
  140. // create new channel category
  141. const channelCategory = new ChannelCategory({
  142. // main data
  143. id: channelCategoryId.toString(),
  144. channels: [],
  145. createdInBlock: event.blockNumber,
  146. // fill in auto-generated fields
  147. createdAt: new Date(fixBlockTimestamp(event.blockTimestamp).toNumber()),
  148. updatedAt: new Date(fixBlockTimestamp(event.blockTimestamp).toNumber()),
  149. // integrate metadata
  150. ...protobufContent,
  151. })
  152. // save channel
  153. await db.save<ChannelCategory>(channelCategory)
  154. // emit log event
  155. logger.info('Channel category has been created', { id: channelCategory.id })
  156. }
  157. // eslint-disable-next-line @typescript-eslint/naming-convention
  158. export async function content_ChannelCategoryUpdated(db: DatabaseManager, event: SubstrateEvent) {
  159. // read event data
  160. const { channelCategoryId, channelCategoryUpdateParameters } = new Content.ChannelCategoryUpdatedEvent(event).data
  161. // load channel category
  162. const channelCategory = await db.get(ChannelCategory, {
  163. where: {
  164. id: channelCategoryId.toString(),
  165. } as FindConditions<ChannelCategory>,
  166. })
  167. // ensure channel exists
  168. if (!channelCategory) {
  169. return inconsistentState('Non-existing channel category update requested', channelCategoryId)
  170. }
  171. // read metadata
  172. const protobufContent = await readProtobuf(new ChannelCategory(), {
  173. metadata: channelCategoryUpdateParameters.new_meta,
  174. db,
  175. event,
  176. })
  177. // update all fields read from protobuf
  178. for (const [key, value] of Object.entries(protobufContent)) {
  179. channelCategory[key] = value
  180. }
  181. // set last update time
  182. channelCategory.updatedAt = new Date(fixBlockTimestamp(event.blockTimestamp).toNumber())
  183. // save channel category
  184. await db.save<ChannelCategory>(channelCategory)
  185. // emit log event
  186. logger.info('Channel category has been updated', { id: channelCategory.id })
  187. }
  188. // eslint-disable-next-line @typescript-eslint/naming-convention
  189. export async function content_ChannelCategoryDeleted(db: DatabaseManager, event: SubstrateEvent) {
  190. // read event data
  191. const { channelCategoryId } = new Content.ChannelCategoryDeletedEvent(event).data
  192. // load channel category
  193. const channelCategory = await db.get(ChannelCategory, {
  194. where: {
  195. id: channelCategoryId.toString(),
  196. } as FindConditions<ChannelCategory>,
  197. })
  198. // ensure channel category exists
  199. if (!channelCategory) {
  200. return inconsistentState('Non-existing channel category deletion requested', channelCategoryId)
  201. }
  202. // delete channel category
  203. await db.remove<ChannelCategory>(channelCategory)
  204. // emit log event
  205. logger.info('Channel category has been deleted', { id: channelCategory.id })
  206. }
  207. /// ///////////////// Helpers ////////////////////////////////////////////////////
  208. function handleChannelRewardAccountChange(
  209. channel: Channel, // will be modified inside of the function!
  210. reward_account: Option<AccountId>
  211. ) {
  212. const rewardAccount = reward_account.unwrapOr(null)
  213. // new different reward account set?
  214. if (rewardAccount) {
  215. channel.rewardAccount = rewardAccount.toString()
  216. return
  217. }
  218. // reward account removed
  219. channel.rewardAccount = undefined // plan deletion (will have effect when saved to db)
  220. }