Advertisement
Guest User

anthony

a guest
Jul 3rd, 2010
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.37 KB | None | 0 0
  1. use Irssi;
  2. use Irssi::Irc;
  3. use strict;
  4. use vars qw($VERSION %IRSSI $DEBUG);
  5.  
  6. #
  7. # autobleh is an irssi helper script for IRC Channel OPs
  8. # and IRC Network Staff.
  9. #
  10. # Version:
  11. # $Id: autobleh.pl 17 2009-03-28 19:34:05Z sysdef $
  12. #
  13. # Contact:
  14. # project website: http://autobleh.projectnet.org/
  15. # support channel: irc://chat.freenode.net/#autobleh
  16. # repository: https://guest:[email protected]/svn/autobleh/
  17. #
  18. # This script is licensed under GPL Version 3
  19. # License - http://www.gnu.org/licenses/gpl.html
  20. #
  21. # Please send all your thanks, money, donations, coffee, code, suggestions,
  22. # fixes and patches or hardware to the projects 'Main Contact' but please
  23. # write an email first.
  24. #
  25. # Sometimes it's possible to directly send gifts to the developer who needs
  26. # the part you want to give away so we can save additional shipment fees.
  27. #
  28. # Please also read the 'Need Help' page of the project first before you send
  29. # your coffee back to Columbia. (example)
  30. #
  31. # You will find your thanks and contributions in-kind listed on the project
  32. # website and details on how it was shared and helped our development or
  33. # developers.
  34. #
  35.  
  36. $VERSION = q$Rev: 17 $;
  37. %IRSSI = (
  38. authors => 'Juergen (sysdef) Heine, Tom (tomaw) Wesley. based on auto_bleh.pl by Don Armstrong',
  39. name => 'autobleh',
  40. description => 'Provides /ak /aq /ab /abr /abrn /arn /amb /amr /at /op /topicset /modeset /af',
  41. license => 'GPL3',
  42. changed => q$Id: autobleh.pl 17 2009-03-28 19:34:05Z sysdef $,
  43. );
  44.  
  45. # CHANGELOG
  46. # ????-??-?? - tomaw - add irssi setting bleh_deop_after_action
  47. # 2008-01-08 - sysdef - add irssi setting bleh_remove_message
  48. # 2008-01-08 - sysdef - add command op
  49. # 2008-01-08 - sysdef - add command topicset
  50. # 2008-01-08 - sysdef - add command modeset
  51. # 2008-01-22 - sysdef - solve the 'Unable to find nick: CHANSERV' error
  52. # 2008-01-22 - sysdef - add aliases /remove
  53. # 2008-01-25 - sysdef - add command af (auto forward / forward ban)
  54.  
  55. # read the config file
  56. open ( CONF, ".irssi/autobleh.conf" ) or Irssi::print("warning: '~/.irssi/autobleh.conf' doesn't exist.");
  57. Irssi::print("loading autobleh config ...");
  58. my $category;
  59. my $config;
  60. my $val;
  61. my $key;
  62. foreach my $line ( <CONF> ) {
  63. chomp $line;
  64. # filter empty lines
  65. if ( $line =~ /^\s*$/ ) {
  66. #Irssi::print("space : $line");
  67. }
  68. # new category
  69. elsif ( $line =~ /^\[/ ) {
  70. $line =~ /\[(.*)\]/;
  71. $category = $1;
  72. #Irssi::print("category : $line ($1)");
  73. }
  74. # filter comments
  75. elsif ( $line =~ /^#\s+/ ) {
  76. #Irssi::print("comment : $line");
  77. }
  78. # get key/value pair
  79. elsif ( $line =~ /^([^ ]+)\s*=\s*([^ ]+)$/ ) {
  80. Irssi::print("from config : $category $1 => $2");
  81. $config->{$category}{$1} = $2;
  82. }
  83. # crap line
  84. else {
  85. Irssi::print("config : $line");
  86. }
  87. }
  88. close CONF;
  89.  
  90. $DEBUG = 1;# unless defined $DEBUG;
  91.  
  92. my ($actions, %defaults);
  93.  
  94. %defaults = (
  95. GET_OP => 1, # Should we try to get opped when we bleh?
  96. USE_CHANSERV => 1, # Should we use chanserv to get opped?
  97. EXPIRE => 6000, # Do not try to do anything if the action is more than 6000 seconds old.
  98. TIMEOUT => 10, # Timeout /at bans after 10 minutes
  99. DEOP => 1, # We want to deop after action
  100. );
  101.  
  102. my %command_bindings = (
  103.  
  104. op => 'cmd_op',
  105.  
  106. ams => 'cmd_modeset',
  107. modeset => 'cmd_modeset',
  108.  
  109. ats => 'cmd_topicset',
  110. topicset => 'cmd_topicset',
  111.  
  112. af => 'cmd_af',
  113. forward => 'cmd_af',
  114.  
  115. ak => 'cmd_ak',
  116. kick => 'cmd_ak',
  117.  
  118. ab => 'cmd_ab',
  119. ban => 'cmd_ab',
  120.  
  121. aq => 'cmd_aq',
  122. quit => 'cmd_aq',
  123.  
  124. ar => 'cmd_ar',
  125. remove => 'cmd_ar',
  126.  
  127. abr => 'cmd_abr',
  128. removeban => 'cmd_abr',
  129.  
  130. abk => 'cmd_abk',
  131. kickban => 'cmd_abk',
  132.  
  133. abrn => 'cmd_abrn',
  134. removeban_notice => 'cmd_abrn',
  135.  
  136. abkn => 'cmd_abkn',
  137. kickban_notice => 'cmd_abkn',
  138.  
  139. arn => 'cmd_arn',
  140. remove_notice => 'cmd_arn',
  141.  
  142. amb => 'cmd_amb',
  143. massban => 'cmd_amb',
  144.  
  145. amr => 'cmd_amr',
  146. massremove => 'cmd_amr',
  147.  
  148. at => 'cmd_at',
  149. quiet_temp => 'cmd_at',
  150.  
  151. );
  152.  
  153. my %bans_to_remove;
  154.  
  155. sub cmd_op {
  156. my ($data, $server, $witem) = @_;
  157. # return get_op($data,$server,$witem);
  158. return do_bleh('op',$data,$server,$witem);
  159. }
  160.  
  161. sub cmd_modeset {
  162. my ($data, $server, $witem) = @_;
  163. return do_bleh('modeset',$data,$server,$witem);
  164. }
  165.  
  166. sub cmd_topicset {
  167. my ($data, $server, $witem) = @_;
  168. return do_bleh('topicset',$data,$server,$witem);
  169. }
  170.  
  171. sub cmd_at {
  172. my ($data, $server, $witem) = @_;
  173. return do_bleh('timeout',$data,$server,$witem);
  174. }
  175.  
  176. sub cmd_ak {
  177. my ($data, $server, $witem) = @_;
  178. return do_bleh('kick',$data,$server,$witem);
  179. }
  180.  
  181. sub cmd_af {
  182. my ($data, $server, $witem) = @_;
  183.  
  184. if ( defined $config->{forwardban}{$witem->{name}} ) {
  185. # channel -> $config->{forwardban}{$witem->{name}}
  186. my $targetchannel = $witem;
  187. my $channel = $server->channel_find($config->{forwardban}{$witem->{name}});
  188. if (!$channel) {
  189. Irssi::print("Error: i'm not in channel ".$config->{forwardban}{$witem->{name}}." right now.");
  190. return;
  191. }
  192. # TODO: Check if channel is +i and not +Q
  193. do_bleh('modeset',"+I ".$data,$server,$channel);
  194. return do_bleh('forward, kick',$data,$server,$witem);
  195. }
  196. else {
  197. Irssi::print("Error: forward channel for $witem->{name} was not set. Please edit section [forwardban] in ~/.irssi/autobleh.conf");
  198. }
  199. }
  200.  
  201. sub cmd_abk {
  202. my ($data, $server, $witem) = @_;
  203. return do_bleh('kick,ban',$data,$server,$witem);
  204. }
  205.  
  206. sub cmd_abkn {
  207. my ($data, $server, $witem) = @_;
  208. return do_bleh('kick,ban,notice',$data,$server,$witem);
  209. }
  210.  
  211. sub cmd_amb{
  212. my ($data, $server, $witem) = @_;
  213. my @nicks = split /\s+/, $data;
  214. for (@nicks) {
  215. next unless /\w/;
  216. do_bleh('ban',$_,$server,$witem);
  217. }
  218. }
  219.  
  220. sub cmd_ab {
  221. my ($data, $server, $witem) = @_;
  222. return do_bleh('ban',$data,$server,$witem);
  223. }
  224.  
  225. sub cmd_aq {
  226. my ($data, $server, $witem) = @_;
  227. return do_bleh('quiet',$data,$server,$witem);
  228. }
  229.  
  230. sub cmd_amr{
  231. my ($data, $server, $witem) = @_;
  232. my @nicks = split /\s+/, $data;
  233. for (@nicks) {
  234. next unless /\w/;
  235. do_bleh('remove',$_,$server,$witem);
  236. }
  237. }
  238.  
  239. sub cmd_ar {
  240. my ($data, $server, $witem) = @_;
  241. return do_bleh('remove',$data,$server,$witem);
  242. }
  243.  
  244. sub cmd_abr{
  245. my ($data, $server, $witem) =@_;
  246. return do_bleh('remove,ban',$data,$server,$witem);
  247. }
  248.  
  249. sub cmd_abrn{
  250. my ($data, $server, $witem) =@_;
  251. return do_bleh('remove,ban,notice',$data,$server,$witem);
  252. }
  253.  
  254. sub cmd_arn{
  255. my ($data, $server, $witem) =@_;
  256. return do_bleh('remove,notice',$data,$server,$witem);
  257. }
  258.  
  259. sub do_bleh {
  260. my ($cmd, $data, $server, $witem, $duration) = @_;
  261.  
  262. if (!$server || !$server->{connected}) {
  263. Irssi::print("Not connected to server");
  264. return;
  265. }
  266.  
  267. # fix the error: "Can't use string ("0") as a HASH ref while "strict refs" in use at..."
  268. if ( $witem eq 0 ) {
  269. Irssi::print("Can't autokick on a non-channel.");
  270. return;
  271. }
  272.  
  273. if ($witem->{type} ne 'CHANNEL') {
  274. Irssi::print("Can't autokick on a non-channel. [$witem->{type}]");
  275. return;
  276. }
  277.  
  278. # set the network that we're on, the channel and the nick to kick
  279. # once we've been opped
  280.  
  281. $data =~ /^\s*([^\s]+)\s*(\d+)?\s*(.+?|)\s*$/;
  282. my $nick = $1;
  283. my $timeout = $2;
  284. my $reason = $3;
  285. $timeout = $defaults{TIMEOUT} if not defined $timeout or $timeout eq '';
  286.  
  287. if ( $cmd =~ /^(topicset|modeset)$/ ) {
  288. $reason = $nick.' '.$reason;
  289. $nick = "CHANSERV";
  290. }
  291. else {
  292. $reason = Irssi::settings_get_str('bleh_remove_message') if not defined $reason or $reason eq '';
  293. }
  294.  
  295. my $nick_rec = $witem->nick_find($nick);
  296. if ( $nick ne 'CHANSERV' ) {
  297. if (not defined $nick_rec) {
  298. Irssi::print("Unable to find nick: $nick");
  299. return;
  300. }
  301. }
  302.  
  303. my $hostname = $nick_rec->{host} if defined $nick_rec;
  304. if ( $nick ne 'CHANSERV' ) {
  305. Irssi::print("Unable to find hostname for $nick") if not defined $hostname or $hostname eq '';
  306. }
  307. my $username = $hostname;
  308. $hostname =~ s/.+\@//;
  309. $username =~ s/@.*//;
  310. $username =~ s/.*=//;
  311.  
  312. Irssi::print("Nick set to '$nick' from '$data', reason set to '$reason'.") if $DEBUG;
  313.  
  314. my $action = {
  315. type => $cmd,
  316. nick => $nick,
  317. nick_rec => $nick_rec,
  318. network => $witem->{server}->{chatnet},
  319. server => $witem->{server},
  320. completed => 0,
  321. inserted => time,
  322. channel => $witem->{name},
  323. reason => $reason,
  324. hostname => $hostname,
  325. username => $username,
  326. timeout => $timeout,
  327. };
  328.  
  329. Irssi::print(i_want($action)) if $DEBUG;
  330.  
  331. if ($witem->{chanop}) {
  332. Irssi::print("DEBUG: take action $action") if $DEBUG;
  333. take_action($action,$server,$witem);
  334. }
  335. else {
  336. Irssi::print("DEBUG: try to get op in $action->{channel}") if $DEBUG;
  337. $actions->{$data.$action->{inserted}}=$action;
  338. get_op($server, $action->{channel}) if $defaults{GET_OP};
  339. }
  340.  
  341. }
  342.  
  343. sub get_op {
  344. my ($server,$channel) = @_;
  345.  
  346. Irssi::print("QUOTE CS op $channel") if $DEBUG;
  347. $server->command("QUOTE CS op $channel") if $defaults{USE_CHANSERV};
  348. }
  349.  
  350. sub i_want {
  351. my $action = shift;
  352. return "I've wanted to $action->{type} $action->{nick} in $action->{channel} on $action->{network} since $action->{inserted}";
  353. }
  354.  
  355. sub take_action {
  356. my ($action,$server,$channel) = @_;
  357.  
  358. local $_ = $action->{type};
  359. # Now support multiple actions against a single nick (to FE, kick ban, or remove ban). See /abr foo
  360.  
  361. if ( /op/ ) {
  362. Irssi::print( "Give OP only" ) if $DEBUG;
  363. %defaults->{DEOP} = 0;
  364. }
  365.  
  366. if ( /modeset/ ) {
  367. Irssi::print( "Setmode on $action->{channel}" ) if $DEBUG;
  368. # Set channel mode
  369. $channel->command( "/mode ". $action->{reason} );
  370. }
  371.  
  372. if ( /topicset/ ) {
  373. Irssi::print( "Settopic on $action->{channel}" ) if $DEBUG;
  374. # Set topic
  375. $channel->command( "/topic ". $action->{reason} );
  376. # we have to deop us here becaue we receive no modechange
  377. if ( Irssi::settings_get_bool('bleh_deop_after_action') ) {
  378. Irssi::print("MODE $channel->{name} -o $channel->{ownnick}->{nick}") if $DEBUG;
  379. $channel->command("/deop $channel->{ownnick}->{nick}");
  380. }
  381. }
  382.  
  383. if (/timeout/) {
  384. if ($action->{hostname} =~ /gateway\/web\/.+/) {
  385. Irssi::print("Quieting $action->{nick} on $action->{channel} with username $action->{username} for $action->{timeout} seconds") if $DEBUG;
  386. $channel->command("/quote MODE $action->{channel} +q *!*=".$action->{username}."@*") if $action->{username} ne ''; #quiet username
  387. }
  388. else {
  389. Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname} for $action->{timeout} seconds") if $DEBUG;
  390. $channel->command("/quote MODE $action->{channel} +q *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname
  391. }
  392. $bans_to_remove{"$action->{nick}$action->{inserted}"} = $action;
  393. # don't deop on a short time
  394. if ( $action->{timeout} < Irssi::settings_get_str('bleh_at_stay_opped') ) {
  395. Irssi::print("I'll stay opped until unquiet because $action->{timeout} < ".Irssi::settings_get_str('bleh_at_stay_opped')) if $DEBUG;
  396. %defaults->{DEOP} = 0;
  397. }
  398. else {
  399. Irssi::print("I'll NOT stay opped until unquiet because $action->{timeout} < ".Irssi::settings_get_str('bleh_at_stay_opped')) if $DEBUG;
  400. }
  401. }
  402.  
  403. if (/quiet/) {
  404. if ($action->{hostname} =~ /gateway\/web\/.+/) {
  405. Irssi::print("Quieting $action->{nick} on $action->{channel} with username $action->{username}") if $DEBUG;
  406. # Find hostname
  407. $channel->command("/quote MODE $action->{channel} +q *!*=".$action->{username}."@*") if $action->{username} ne ''; #quiet hostname
  408. }
  409. else {
  410. Irssi::print("Quieting $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG;
  411. # Find hostname
  412. $channel->command("/quote MODE $action->{channel} +q *!*@".$action->{hostname}) if $action->{hostname} ne ''; #quiet hostname
  413. }
  414. }
  415.  
  416. if (/ban/) {
  417. if ($action->{hostname} =~ /gateway\/web\/.+/) {
  418. Irssi::print("Banning $action->{nick} from $action->{channel} with username $action->{username}") if $DEBUG;
  419. $channel->command("/quote MODE $action->{channel} +b *!*=".$action->{username}."@*") if $action->{username} ne ''; # ban username
  420. }
  421. else {
  422. Irssi::print("Banning $action->{nick} from $action->{channel} with hostname $action->{hostname}") if $DEBUG;
  423. $channel->command("/quote MODE $action->{channel} +b *!*@".$action->{hostname}) if $action->{hostname} ne ''; # ban hostname
  424. }
  425. }
  426.  
  427. if (/set_invite/) {
  428. Irssi::print("Set +I for $action->{nick} in $action->{channel} with hostname $action->{hostname}") if $DEBUG;
  429. $channel->command("/quote MODE $action->{channel} +I $action->{nick}!*@".$action->{hostname})
  430. if $action->{hostname} ne '';
  431. }
  432.  
  433. if (/forward/) {
  434. if ( defined $config->{forwardban}{$action->{channel}} ) {
  435. my $fwchan = $config->{forwardban}{$action->{channel}};
  436. Irssi::print("Forward ".$action->{nick}." from ".$action->{channel}." to $fwchan with hostname ".$action->{hostname}) if $DEBUG;
  437. # ban hostname
  438. $channel->command("/quote MODE $action->{channel} +b $action->{nick}!*@".$action->{hostname}.'!'.${fwchan}) if $action->{hostname} ne '';
  439. # # invite user to fwchan
  440. # Irssi::print("INVITE user to $fwchan") if $DEBUG;
  441. # $channel->command("/quote invite $action->{nick} $fwchan");
  442. # notice user
  443. $channel->command("/NOTICE $action->{nick} You got a FORWARD BAN from $action->{channel} to $fwchan. Please rejoin channel $action->{channel}");
  444. }
  445. else {
  446. Irssi::print("ATTENTION: add line \"".$action->{channel}." <targetchannel>\" to [forwardban] section in ./irssi/autobleh.conf");
  447. return;
  448. }
  449. }
  450.  
  451. if (/kick/) {
  452. Irssi::print("Kicking $action->{nick} from $action->{channel}") if $DEBUG;
  453. #wtf? -> if ($action->{reason} =~ /\s/) {
  454. $channel->command("/quote KICK $action->{channel} $action->{nick} :$action->{reason}");
  455. #}
  456. #else {
  457. # $channel->command("/quote REMOVE $action->{channel} $action->{nick} $action->{reason}");
  458. #}
  459. }
  460.  
  461. if (/remove/) {
  462. Irssi::print("Removing $action->{nick} from $action->{channel}") if $DEBUG;
  463. if ($action->{reason} =~ /\s/) {
  464. $channel->command("/quote REMOVE $action->{channel} $action->{nick} :$action->{reason}");
  465. } else {
  466. $channel->command("/quote REMOVE $action->{channel} $action->{nick} $action->{reason}");
  467. }
  468. }
  469.  
  470. if (/notice/) {
  471. Irssi::print("Noticing $action->{nick} with $action->{reason}") if $DEBUG;
  472. $channel->command("/NOTICE $action->{nick} $action->{reason}");
  473. }
  474.  
  475. if (/teiuq/) {
  476. if ($action->{hostname} =~ /gateway\/web\/.+/) {
  477. Irssi::print("Unquieting $action->{nick} on $action->{channel} with username $action->{username}") if $DEBUG;
  478. $channel->command("/quote MODE $action->{channel} -q *!*=".$action->{username}."@*");
  479. }
  480. else {
  481. Irssi::print("Unquieting $action->{nick} on $action->{channel} with hostname $action->{hostname}") if $DEBUG;
  482. $channel->command("/quote MODE $action->{channel} -q *!*@".$action->{hostname});
  483. }
  484. }
  485. return; #for now.
  486. }
  487.  
  488. sub deop_us {
  489. my ($rec) = @_;
  490. my $channel = $rec->{channel};
  491. my $server = $rec->{server};
  492.  
  493. Irssi::print("MODE $channel->{name} -o $channel->{ownnick}->{nick}") if $DEBUG;
  494. $channel->command("/deop $channel->{ownnick}->{nick}");
  495. }
  496.  
  497. sub sig_mode_change {
  498. my ($channel,$nick) = @_;
  499.  
  500. # Are there any actions to process?
  501. # See if we got opped.
  502. return if scalar(keys %$actions) eq 0;
  503.  
  504. my @deop_array;
  505. if ($channel->{server}->{nick} eq $nick->{nick} and $nick->{op}) {
  506. Irssi::print("We've been opped") if $DEBUG;
  507. foreach (keys %$actions) {
  508. # See if this action is too old
  509. if (time - $actions->{$_}->{inserted} > $defaults{EXPIRE}) {
  510. Irssi::print("Expiring action: \"".i_want($actions->{$_})."\" because of time") if $DEBUG;
  511. delete $actions->{$_};
  512. next;
  513. }
  514. Irssi::print(i_want($actions->{$_})) if $DEBUG;
  515. # Find the server to take action on
  516. my $server = $actions->{$_}->{server};
  517. Irssi::print("Unable to find server for chatnet: $actions->{$_}->{network}") and return if not defined $server;
  518. Irssi::print("Found server for chatnet: $actions->{$_}->{network}") if $DEBUG;
  519. # Find the channel to take action on
  520. my $channel = $server->channel_find($actions->{$_}->{channel});
  521. Irssi::print("Unable to find channel for channel: $actions->{$_}->{channel}") and return if not defined $channel;
  522. Irssi::print("Found channel for channel: $actions->{$_}->{channel}") if $DEBUG;
  523. Irssi::print("We are opped on the channel!") if $DEBUG;
  524. take_action($actions->{$_},$server,$channel);
  525. if ( Irssi::settings_get_bool('bleh_deop_after_action') and %defaults->{DEOP} eq 1 ) {
  526. push @deop_array,{server=>$server,channel=>$channel};
  527. }
  528. %defaults->{DEOP} = 1;
  529. delete $actions->{$_}; # Do not repeat this action.
  530. }
  531. foreach (@deop_array) {
  532. deop_us($_);
  533. }
  534. } else {
  535. Irssi::print("Fooey. Not opped.") if $DEBUG;
  536. }
  537. }
  538.  
  539. sub try_to_remove_bans {
  540. return unless keys %bans_to_remove;
  541. for my $key (keys %bans_to_remove) {
  542. if (($bans_to_remove{$key}{inserted} + $bans_to_remove{$key}{timeout}) < time) {
  543. $bans_to_remove{$key}{type} = 'teiuq'; #unquiet
  544. $actions->{$key} = $bans_to_remove{$key};
  545. delete $bans_to_remove{$key};
  546. # TODO: only try to get op if we're not opped
  547. # if ($witem->{chanop}) {
  548. # Irssi::print("DEBUG: take action $action") if $DEBUG;
  549. # take_action($action,$server,$witem);
  550. # }
  551. # else {
  552. # Irssi::print("DEBUG: try to get op in $action->{channel}") if $DEBUG;
  553. # $actions->{$data.$action->{inserted}}=$action;
  554. # get_op($server, $action->{channel}) if $defaults{GET_OP};
  555. # }
  556. get_op($actions->{$key}{server}, $actions->{$key}{channel}) if $defaults{GET_OP};
  557. }
  558. }
  559. }
  560.  
  561. # call the try to remove bans function every minute
  562. Irssi::timeout_add(1000*5,'try_to_remove_bans',undef);
  563. Irssi::signal_add_last('nick mode changed','sig_mode_change');
  564. my ($command,$function);
  565.  
  566. while (($command,$function) = each %command_bindings) {
  567. Irssi::command_bind($command,$function, 'bleh');
  568. }
  569.  
  570. Irssi::settings_add_bool($IRSSI{name}, 'bleh_deop_after_action', 1);
  571. Irssi::settings_add_str($IRSSI{name}, 'bleh_remove_message', 'you should know better');
  572. Irssi::settings_add_str($IRSSI{name}, 'bleh_at_stay_opped', 10);
  573.  
  574. # find text for antispam
  575. #Irssi::signal_add_last( "message public", "msg_public" );
  576.  
  577. 1;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement