Contract Name:
RSS2PodcastEngagementV2
Contract Source Code:
File 1 of 1 : RSS2PodcastEngagementV2
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface RSS2PodcastEngagementToken {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
}
contract RSS2PodcastEngagementV2 {
enum EngagementType {
FEEDBACK,
RATING,
SHARE_TWITTER,
SHARE_LINKEDIN,
SUGGESTION,
FIRST_COMMENT,
QUALITY_FEEDBACK,
WEEKLY_ACTIVE,
STREAK_BONUS,
COMPLETION,
REFERRAL,
COMMUNITY_CHOICE,
DISCUSSION_STARTER
}
enum ModerationType {
HATEFUL_CONTENT,
SPAM,
INAPPROPRIATE,
MISLEADING,
OTHER
}
struct Episode {
uint256 feedbackCount;
uint256 totalRating;
address firstCommenter;
mapping(uint256 => uint256) feedbackVotes;
}
struct Feedback {
address user;
string content;
bool isHidden;
uint256 timestamp;
uint256 replyCount;
bool isDiscussionStarter;
ModerationType moderationType;
uint256 reportCount;
}
address public owner;
RSS2PodcastEngagementToken public dixoToken;
mapping(address => bool) public moderators;
mapping(address => bool) public bannedUsers;
mapping(EngagementType => uint256) public rewardAmounts;
mapping(uint256 => Episode) public episodes;
mapping(uint256 => Feedback[]) public episodeFeedback;
mapping(uint256 => mapping(address => mapping(EngagementType => bool))) public userEngagements;
mapping(uint256 => mapping(address => string)) public shareProofs;
mapping(uint256 => mapping(uint256 => mapping(address => bool))) public hasVotedOnFeedback;
mapping(address => uint256) public communityPoints;
mapping(address => uint256) public userLastEngagement;
mapping(address => uint256) public weeklyEngagementCount;
mapping(address => uint256) public streakCount;
mapping(address => address) public referredBy;
mapping(address => uint256) public userReportCount;
mapping(uint256 => mapping(uint256 => mapping(address => bool))) public hasReportedFeedback;
uint256 public constant REPORT_THRESHOLD = 5;
event ModeratorAdded(address moderator);
event ModeratorRemoved(address moderator);
event UserBanned(address user, string reason);
event UserUnbanned(address user);
event FeedbackSubmitted(uint256 indexed episodeId, address user, uint256 feedbackIndex);
event FeedbackModerated(uint256 indexed episodeId, uint256 feedbackIndex, ModerationType moderationType);
event FeedbackReported(uint256 indexed episodeId, uint256 feedbackIndex, address reporter);
event RewardEarned(address user, uint256 indexed episodeId, EngagementType engagementType, uint256 amount);
event RewardAmountUpdated(EngagementType engagementType, uint256 oldAmount, uint256 newAmount);
event ShareSubmitted(address user, uint256 indexed episodeId, string platform, string proofHash);
event RatingSubmitted(uint256 indexed episodeId, address user, uint8 rating);
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
modifier onlyModerator() {
require(moderators[msg.sender], "Not moderator");
_;
}
modifier notBanned() {
require(!bannedUsers[msg.sender], "User is banned");
_;
}
constructor(address _dixoToken) {
owner = msg.sender;
moderators[msg.sender] = true;
dixoToken = RSS2PodcastEngagementToken(_dixoToken);
}
function addModerator(address moderator) external onlyOwner {
require(!moderators[moderator], "Already moderator");
moderators[moderator] = true;
emit ModeratorAdded(moderator);
}
function removeModerator(address moderator) external onlyOwner {
require(moderator != owner, "Cannot remove owner");
require(moderators[moderator], "Not moderator");
moderators[moderator] = false;
emit ModeratorRemoved(moderator);
}
function banUser(address user, string calldata reason) external onlyModerator {
require(!bannedUsers[user], "Already banned");
bannedUsers[user] = true;
emit UserBanned(user, reason);
}
function unbanUser(address user) external onlyModerator {
require(bannedUsers[user], "Not banned");
bannedUsers[user] = false;
emit UserUnbanned(user);
}
function submitFeedback(
uint256 episodeId,
string calldata feedback
) external notBanned {
require(episodeId > 0, "Episode ID invalid");
require(!userEngagements[episodeId][msg.sender][EngagementType.FEEDBACK], "Already gave feedback");
require(bytes(feedback).length >= 50, "Feedback too short");
uint256 feedbackIndex = episodeFeedback[episodeId].length;
episodeFeedback[episodeId].push(Feedback({
user: msg.sender,
content: feedback,
isHidden: false,
timestamp: block.timestamp,
replyCount: 0,
isDiscussionStarter: false,
moderationType: ModerationType.OTHER,
reportCount: 0
}));
userEngagements[episodeId][msg.sender][EngagementType.FEEDBACK] = true;
episodes[episodeId].feedbackCount++;
if (episodes[episodeId].feedbackCount == 1) {
episodes[episodeId].firstCommenter = msg.sender;
_distributeReward(msg.sender, EngagementType.FIRST_COMMENT);
}
_distributeReward(msg.sender, EngagementType.FEEDBACK);
_updateEngagementMetrics(msg.sender);
emit FeedbackSubmitted(episodeId, msg.sender, feedbackIndex);
}
function submitRating(uint256 episodeId, uint8 rating) external notBanned {
require(episodeId > 0, "Episode ID invalid");
require(!userEngagements[episodeId][msg.sender][EngagementType.RATING], "Already rated");
require(rating >= 1 && rating <= 5, "Invalid rating");
userEngagements[episodeId][msg.sender][EngagementType.RATING] = true;
episodes[episodeId].totalRating += rating;
_distributeReward(msg.sender, EngagementType.RATING);
_updateEngagementMetrics(msg.sender);
emit RatingSubmitted(episodeId, msg.sender, rating);
}
function moderateFeedback(
uint256 episodeId,
uint256 feedbackIndex,
ModerationType moderationType
) external onlyModerator {
require(feedbackIndex < episodeFeedback[episodeId].length, "Invalid feedback index");
Feedback storage feedback = episodeFeedback[episodeId][feedbackIndex];
feedback.isHidden = true;
feedback.moderationType = moderationType;
emit FeedbackModerated(episodeId, feedbackIndex, moderationType);
}
function reportFeedback(
uint256 episodeId,
uint256 feedbackIndex
) external notBanned {
require(!hasReportedFeedback[episodeId][feedbackIndex][msg.sender], "Already reported");
require(episodeFeedback[episodeId][feedbackIndex].user != msg.sender, "Cannot report own feedback");
Feedback storage feedback = episodeFeedback[episodeId][feedbackIndex];
feedback.reportCount++;
hasReportedFeedback[episodeId][feedbackIndex][msg.sender] = true;
if (feedback.reportCount >= REPORT_THRESHOLD) {
feedback.isHidden = true;
}
emit FeedbackReported(episodeId, feedbackIndex, msg.sender);
}
function setRewardAmount(EngagementType engagementType, uint256 amount) external onlyOwner {
uint256 oldAmount = rewardAmounts[engagementType];
rewardAmounts[engagementType] = amount;
emit RewardAmountUpdated(engagementType, oldAmount, amount);
}
function setBatchRewardAmounts(
EngagementType[] calldata engagementTypes,
uint256[] calldata amounts
) external onlyOwner {
require(engagementTypes.length == amounts.length, "Length mismatch");
for(uint i = 0; i < engagementTypes.length; i++) {
uint256 oldAmount = rewardAmounts[engagementTypes[i]];
rewardAmounts[engagementTypes[i]] = amounts[i];
emit RewardAmountUpdated(engagementTypes[i], oldAmount, amounts[i]);
}
}
function _distributeReward(
address user,
EngagementType engagementType
) internal {
uint256 rewardAmount = rewardAmounts[engagementType];
if (rewardAmount > 0) {
require(dixoToken.transfer(user, rewardAmount), "Reward transfer failed");
emit RewardEarned(user, 0, engagementType, rewardAmount);
}
}
function _updateEngagementMetrics(address user) internal {
uint256 currentWeek = block.timestamp / 1 weeks;
uint256 lastWeek = userLastEngagement[user] / 1 weeks;
if (currentWeek > lastWeek) {
weeklyEngagementCount[user] = 1;
if (currentWeek == lastWeek + 1) {
streakCount[user]++;
if (streakCount[user] % 3 == 0) {
_distributeReward(user, EngagementType.STREAK_BONUS);
}
} else {
streakCount[user] = 1;
}
} else {
weeklyEngagementCount[user]++;
if (weeklyEngagementCount[user] == 3) {
_distributeReward(user, EngagementType.WEEKLY_ACTIVE);
}
}
userLastEngagement[user] = block.timestamp;
}
function getAllRewardAmounts() external view returns (uint256[] memory) {
uint256[] memory amounts = new uint256[](13);
for(uint i = 0; i < 13; i++) {
amounts[i] = rewardAmounts[EngagementType(i)];
}
return amounts;
}
function getFeedbackDetails(
uint256 episodeId,
uint256 feedbackIndex
) external view returns (
address user,
string memory content,
bool isHidden,
uint256 timestamp,
uint256 reportCount,
ModerationType moderationType
) {
Feedback storage feedback = episodeFeedback[episodeId][feedbackIndex];
return (
feedback.user,
feedback.content,
feedback.isHidden,
feedback.timestamp,
feedback.reportCount,
feedback.moderationType
);
}
function transferOwnership(address newOwner) external onlyOwner {
require(newOwner != address(0), "Invalid address");
owner = newOwner;
}
}