This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

Birdy (OAuth, Twitter, Tweets&MegaHAL v6.01d) Jun12,2013

Support & discussion of released scripts, and announcements of new releases.
Post Reply
N
Nickmman
Voice
Posts: 6
Joined: Mon Jun 20, 2011 9:15 pm
Location: Montevideo, Uruguay

Post by Nickmman »

speechles wrote:
Nickmman wrote:Yep, starts with #nick. Did everything you told me to, still get the same error:
The issue I think, is that channel. You will need to:
.-chan #nick

Removing it, then... after this..
.+chan #nick

Adding it back, all your users, all your chansets, all your settings..
This script uses [channel get $chan setting] to check chanset controls. This method requires dynamically created channels, only.

Also, the correct command to show the error is:

.set $::errorInfo

type that, after one of those errors and paste the reply. I'm betting it's going to accuse the [channel get] command which I've explained above how to correct in your situation.
Still get the same error:

Code: Select all

.-chan #nick
#nick is no longer a valid channel, changing your console to '*'
Channel #nick removed from the bot.
This includes any channel specific bans, invites, exemptions and user records that you set.
[10:59:17] #Nick# -chan #nick
.+chan #nick
[10:59:25] #Nick# +chan #nick
[10:59:26] NickBot joined #Nick.
[10:59:26] #Nick: mode change '+o NickBot' by ChanServ!ChanServ@Services.VoltFire.net
.chanset #nick +twitter
.chanset #nick +twitterfriends
.chanset #nick +twittermentions
.chanset #nick +twittertrackSuccessfully set modes { +twitter  } on #nick.
[10:59:36] #Nick# chanset #nick +twitter
Successfully set modes { +twitterfriends  } on #nick.
[10:59:36] #Nick# chanset #nick +twitterfriends
Successfully set modes { +twittermentions  } on #nick.
[10:59:36] #Nick# chanset #nick +twittermentions

Successfully set modes { +twittertrack  } on #nick.
[10:59:39] #Nick# chanset #nick +twittertrack
[11:00:00] Tcl error [proc:twitter:friendsauto]: no such channel record
[11:00:01] Tcl error [proc:twitter:megahal:privatereply]: no such channel record
[11:00:01] @#NickBot (+mCtn) : [m/5 o/3 h/0 v/2 n/0 b/0 e/0 I/0]
[11:00:01] @#Irpg (+CtNn) : [m/14 o/13 h/0 v/0 n/1 b/0 e/0 I/0]
[11:00:01] @#nick (+tnl 22) : [m/13 o/11 h/0 v/2 n/0 b/0 e/0 I/0]
.set $::errorInfo
[11:00:05] #Nick# set $::errorInfo
Error: can't read "no such channel record
    while executing
"channel get $chan twitter"
    (procedure "proc:twitter:megahal:privatereply" line 8)
    invoked from within
"proc:twitter:megahal:privatereply $_time1 $_time2 $_time3 $_time4 $_time5"": no such variable
User avatar
speechles
Revered One
Posts: 1398
Joined: Sat Aug 26, 2006 10:19 pm
Location: emerald triangle, california (coastal redwoods)

Post by speechles »

Nickmman wrote:Yep, starts with #nick. Did everything you told me to, still get the same error:
To stop the error with friends, and find out exactly what is going on...

Code: Select all

proc proc:twitter:friendsauto {args} {
	foreach entry $::twitter(accts) {
		set part [split $entry |]
		set chan [lindex $part 0]
		if {![channel get $chan twitterfriends]} { continue }
Find the above, and edit it to look like it does below:

Code: Select all

proc proc:twitter:friendsauto {args} {
	foreach entry $::twitter(accts) {
		set part [split $entry |]
		set chan [lindex $part 0]
		if {![validchan $chan]} {
			putlog "Twitter-Friends: Invalid channel $chan."
			continue
		}
		if {![channel get $chan twitterfriends]} { continue }
To stop the error with mentions:

Code: Select all

# megahal replies to any of your accounts mentions
proc proc:twitter:megahal:privatereply {m h d mo y} {
	global twitter
	if {[info exists twitter(base64)]} { return }
	foreach entry $twitter(accts) {
		set html ""
		set part [split $entry |]
		set chan [lindex $part 0]
		if {![channel get $chan twitter]} { continue }
		if {![channel get $chan twittermentions]} { continue }
Change the above, to look like it does below:

Code: Select all

# megahal replies to any of your accounts mentions
proc proc:twitter:megahal:privatereply {m h d mo y} {
	global twitter
	if {[info exists twitter(base64)]} { return }
	foreach entry $twitter(accts) {
		set html ""
		set part [split $entry |]
		set chan [lindex $part 0]
		if {![validchan $chan]} {
			putlog "Twitter-Mentions: Invalid channel $chan."
			continue
		}
		if {![channel get $chan twitter]} { continue }
		if {![channel get $chan twittermentions]} { continue }
After these changes, save and .rehash your bot. In partyline, it should start announcing the channel is invalid, rather than that error.


Is the channel really named #nick, or is that a placeholder? What version of eggdrop is this? What version of tcl? What platform does this run on?

Kill your eggdrop.
Make sure your eggdrop.conf has no channels set-up within it's static section.
Delete *.chan files from /eggdrop folder
Delete *.user files from /eggdrop folder

Restart your eggdrop from scratch:
eggdrop -m eggdrop.conf

Now message it hello, set a password, and enter partyline.
Now try everything you did again and it hopefully works this time... ;)

It shouldn't be this hard to get birdy to fly around your channel tweeting from every tree top it can find...
N
Nickmman
Voice
Posts: 6
Joined: Mon Jun 20, 2011 9:15 pm
Location: Montevideo, Uruguay

Post by Nickmman »

speechles wrote:
Nickmman wrote:Yep, starts with #nick. Did everything you told me to, still get the same error:
To stop the error with friends, and find out exactly what is going on...

Code: Select all

proc proc:twitter:friendsauto {args} {
	foreach entry $::twitter(accts) {
		set part [split $entry |]
		set chan [lindex $part 0]
		if {![channel get $chan twitterfriends]} { continue }
Find the above, and edit it to look like it does below:

Code: Select all

proc proc:twitter:friendsauto {args} {
	foreach entry $::twitter(accts) {
		set part [split $entry |]
		set chan [lindex $part 0]
		if {![validchan $chan]} {
			putlog "Twitter-Friends: Invalid channel $chan."
			continue
		}
		if {![channel get $chan twitterfriends]} { continue }
To stop the error with mentions:

Code: Select all

# megahal replies to any of your accounts mentions
proc proc:twitter:megahal:privatereply {m h d mo y} {
	global twitter
	if {[info exists twitter(base64)]} { return }
	foreach entry $twitter(accts) {
		set html ""
		set part [split $entry |]
		set chan [lindex $part 0]
		if {![channel get $chan twitter]} { continue }
		if {![channel get $chan twittermentions]} { continue }
Change the above, to look like it does below:

Code: Select all

# megahal replies to any of your accounts mentions
proc proc:twitter:megahal:privatereply {m h d mo y} {
	global twitter
	if {[info exists twitter(base64)]} { return }
	foreach entry $twitter(accts) {
		set html ""
		set part [split $entry |]
		set chan [lindex $part 0]
		if {![validchan $chan]} {
			putlog "Twitter-Mentions: Invalid channel $chan."
			continue
		}
		if {![channel get $chan twitter]} { continue }
		if {![channel get $chan twittermentions]} { continue }
After these changes, save and .rehash your bot. In partyline, it should start announcing the channel is invalid, rather than that error.


Is the channel really named #nick, or is that a placeholder? What version of eggdrop is this? What version of tcl? What platform does this run on?

Kill your eggdrop.
Make sure your eggdrop.conf has no channels set-up within it's static section.
Delete *.chan files from /eggdrop folder
Delete *.user files from /eggdrop folder

Restart your eggdrop from scratch:
eggdrop -m eggdrop.conf

Now message it hello, set a password, and enter partyline.
Now try everything you did again and it hopefully works this time... ;)
Heh, turns out it was something that was commented out but didn't work. Deleted a couple of lines and everything works dandy! Thanks for the help! ^_^ Your script is awesome :D
F
Football
Master
Posts: 205
Joined: Fri Dec 26, 2008 3:08 pm
Location: Quakenet, #Football

Post by Football »

hey speechless,

Was just thinking that a feature that will announce when someone new follows you, would be really nice.
Idling at #Football, Quakenet.
User avatar
speechles
Revered One
Posts: 1398
Joined: Sat Aug 26, 2006 10:19 pm
Location: emerald triangle, california (coastal redwoods)

Post by speechles »

Football wrote:hey speechless,

Was just thinking that a feature that will announce when someone new follows you, would be really nice.
Hmm... Interesting...

.chanset #roms-isos +twitterfollowers

<sp33chy> Lost followers: ndstemp
<sp33chy> 89 users now following! You can too @ http://twitter.com/suqmuhnutz! Follow us at #roms-isos!

<sp33chy> New Followers: Internet_Cancer
<sp33chy> 90 users now following! You can too @ http://twitter.com/suqmuhnutz! Follow us at #roms-isos!

Code: Select all

# Interval in which to check for lost or new followers?
# (time string)
set twitter(followerstimestring) [list "?0*" "?5*"]
...
set twitter(follow-message) "\002%current%\002 users now following! You can too @ http://twitter.com/%acct%! Follow us at %chan%!"
set twitter(follow-new) "\002New followers\002: %output%"
set twitter(follow-lost) "\002Lost followers\002: %output%"
Get the new script here: Birdy 5.05b
Hopefully this is somewhat what you had in mind. This does make it much more interactive as you can immediately respond to your new followers. It tracks your followers and will tell you every 5 minutes (default) which you have lost, and which are new. Remember, to use this feature you must enable it:
.chanset #yourchan +twitterfollowers
F
Football
Master
Posts: 205
Joined: Fri Dec 26, 2008 3:08 pm
Location: Quakenet, #Football

Post by Football »

As always, quality stuff speechless!

Thanks!
Idling at #Football, Quakenet.
F
Football
Master
Posts: 205
Joined: Fri Dec 26, 2008 3:08 pm
Location: Quakenet, #Football

Post by Football »

Hi speechless, that fact isn't accurate (100 users now following)

[19:35:02] <@Soccer> 100 users now following! You can too @ http://twitter.com/ircfootball! Follow us at #premiership!
Idling at #Football, Quakenet.
User avatar
speechles
Revered One
Posts: 1398
Joined: Sat Aug 26, 2006 10:19 pm
Location: emerald triangle, california (coastal redwoods)

Post by speechles »

Football wrote:Hi speechless, that fact isn't accurate (100 users now following)

[19:35:02] <@Soccer> 100 users now following! You can too @ http://twitter.com/ircfootball! Follow us at #premiership!
Yes, I'm sorry for that. I never realized twitter had deprecated the methods I'm using to acquire "friends" and "followers". This deprecation leads to the situation you are experiencing. So in the meantime, please ".chanset #yourchan -twitterfollowers" and stop using it. Or conversely, change twitter(follower-message) to "". An empty "", nothing inside, this will make eggdrop not show that line. It will still announce your new followers and lost followers this way, just not the message.

Also, !friends and !followers will not work correctly for you either. This is because of twitter deprecating the methods I used to acquire these lists. They offer an alternative, which is a combination of web fetches. This will obviously consume more rate-calls, also slowing down things a little bit having to recurse several pages to acquire this information, rather than just a single fetch.

Very shortly, I will have a proper combination of methods to once again have !friends, !followers, and follower tracking working properly. But until that time, all I can suggest is what I already have above, and advise you to remember when using !friends/!followers it likely isn't correct either.

Stay tuned.... and expect something shortly. :)
F
Football
Master
Posts: 205
Joined: Fri Dec 26, 2008 3:08 pm
Location: Quakenet, #Football

Post by Football »

If you're at it, theres something else I`m not quite sure about.
set twitter(restapi-display-cont) "\002%name%\002: %text% - ERROX"
set twitter(track-display-cont) "\002%screen%\002: %text% - ERROR 15"
Ive added comments ('ERROR X' and 'ERROR 15' to the end of those output lines because I`m not quite sure what they are used for, when the script outputs it, sometimes it seems to be corrupted or something

Here are some examples:
[00:36:03] <@Soccer> Anfield Index: - ERROR X
[00:20:05] <@Soccer> efootballforum: - ERORR 15
The original tweet was:
Basic @RyanBabel Stats 07/08: Mins: 1415 Goals: 4 Assists: 4 Chances created: 24 Pass Completed : 79% Cross Accuracy: 19% Shot Accuracy: 36%

The output was:
[00:00:08] <@Soccer> (04Anfield Index): Basic @RyanBabel Stats 07/08:
[00:00:10] <@Soccer> Anfield Index: Mins: 1415 - ERROR X
[00:00:12] <@Soccer> Anfield Index: Goals: 4 - ERROR X
[00:00:14] <@Soccer> Anfield Index: Assists: 4 - ERROR X
[00:00:16] <@Soccer> Anfield Index: Chances created: 24 - ERROR X
[00:00:18] <@Soccer> Anfield Index: Pass Completed : 79% - ERROR X
[00:00:20] <@Soccer> Anfield Index: Cross Accuracy: 19% - ERROR X
[00:00:22] <@Soccer> Anfield Index: Shot Accuracy: 36% - ERROR X
Is that supposed to work like that?
Idling at #Football, Quakenet.
User avatar
speechles
Revered One
Posts: 1398
Joined: Sat Aug 26, 2006 10:19 pm
Location: emerald triangle, california (coastal redwoods)

Post by speechles »

Football wrote:The original tweet was:
Basic @RyanBabel Stats 07/08: Mins: 1415 Goals: 4 Assists: 4 Chances created: 24 Pass Completed : 79% Cross Accuracy: 19% Shot Accuracy: 36%

The output was:
[00:00:08] <@Soccer> (04Anfield Index): Basic @RyanBabel Stats 07/08:
[00:00:10] <@Soccer> Anfield Index: Mins: 1415 - ERROR X
[00:00:12] <@Soccer> Anfield Index: Goals: 4 - ERROR X
[00:00:14] <@Soccer> Anfield Index: Assists: 4 - ERROR X
[00:00:16] <@Soccer> Anfield Index: Chances created: 24 - ERROR X
[00:00:18] <@Soccer> Anfield Index: Pass Completed : 79% - ERROR X
[00:00:20] <@Soccer> Anfield Index: Cross Accuracy: 19% - ERROR X
[00:00:22] <@Soccer> Anfield Index: Shot Accuracy: 36% - ERROR X
Is that supposed to work like that?
Yes, the tweet contains "newlines" and this script can render those newlines. Twitter Web will not render these newlines. Twitter Web instead changes them to "spaces".

This is how it should look:
<speechles> !tsearch "Basic @RyanBabel Stats"
<sp33chy> ( 4-1 ) Search timelines ( #roms-isos@suqmuhnutz ):
<sp33chy> AnfieldIndex: Basic @RyanBabel Stats 07/08: ( 91612878678929408@AnfieldIndex - 10h, 5m, 34s ago via Twitter for Mac )
<sp33chy> AnfieldIndex: Mins: 1415
<sp33chy> AnfieldIndex: Goals: 4
<sp33chy> AnfieldIndex: Assists: 4
<sp33chy> AnfieldIndex: Chances created: 24
<sp33chy> AnfieldIndex: Pass Completed : 79%
<sp33chy> AnfieldIndex: Cross Accuracy: 19%
<sp33chy> AnfieldIndex: hot Accuracy: 36%
<sp33chy> Bob7LFC: RT @AnfieldIndex: Basic @RyanBabel Stats 07/08: ( 91613106089893888@Bob7LFC - 10h, 4m, 40s ago via Twitter for Mac )
<sp33chy> Bob7LFC: Mins: 1415
<sp33chy> Bob7LFC: Goals: 4
<sp33chy> Bob7LFC: Assists: 4
<sp33chy> Bob7LFC: Chances created: 24
<sp33chy> Bob7LFC: Pass Completed : 79%
<sp33chy> Bob7LFC: Cross Accuracy: 19%
<sp33chy> Bob7LFC: Shot Accuracy: 36%
<sp33chy> SelfMadeAbdi: RT @AnfieldIndex Basic @RyanBabel Stats 07/08:Mins: 1415Goals: 4Assists: 4Chances created: 24Pass Completed (cont) http://tl.gd/bnuuaf ( 91613113471860736@SelfMadeAbdi - 10h, 4m, 38s ago via Plume   )
<sp33chy> 2kwik4uwp: RT @AnfieldIndex: Basic @RyanBabel Stats 07/08: ( 91614278574022656@2kwik4uwp - 10h, 1s ago via Twitter for Mac )
<sp33chy> 2kwik4uwp: Mins: 1415
<sp33chy> 2kwik4uwp: Goals: 4
<sp33chy> 2kwik4uwp: Assists: 4
<sp33chy> 2kwik4uwp: Chances created: 24
<sp33chy> 2kwik4uwp: Pass Completed : 79%
<sp33chy> 2kwik4uwp: Cross Accuracy: 19%
<sp33chy> 2kwik4uwp: Shot Accuracy: 36%

<speechles> !rt 91612878678929408
<sp33chy> ReTweet created: http://twitter.com/suqmuhnutz ( 91612878678929408@AnfieldIndex -> 91769200934264832@suqmuhnutz - 10h, 20m, 56s ago via Twitter for Mac )

<speechles> !rtbyme 1-1
<sp33chy> ( 1-1 ) Retweets by me timelines ( #roms-isos@suqmuhnutz ):
<sp33chy> #roms-isos@efnet (RT Anfield Index(3)): RT @AnfieldIndex: Basic @RyanBabel Stats 07/08: ( 91769200934264832@AnfieldIndex - 0s ago via Twitter for Mac )
<sp33chy> #roms-isos@efnet (RT Anfield Index(3)): Mins: 1415
<sp33chy> #roms-isos@efnet (RT Anfield Index(3)): Goals: 4
<sp33chy> #roms-isos@efnet (RT Anfield Index(3)): Assists: 4
<sp33chy> #roms-isos@efnet (RT Anfield Index(3)): Chances created: 24
<sp33chy> #roms-isos@efnet (RT Anfield Index(3)): Pass Completed : 79%
<sp33chy> #roms-isos@efnet (RT Anfield Index(3)): Cross Accuracy: 19%
<sp33chy> #roms-isos@efnet (RT Anfield Index(3)): Shot Accuracy: 36%
The first line, notice, has the "twitter data" attached. The tweet-id @ screen-name as well as how long ago, and the source of the application that submitted that tweet. The rest of the lines, use the "*-cont" template to build their output lines. This is so the rest of the lines also, don't contain the "twitter data". It would be redundant to convey these onto every subsequent line.

Also take note, the application, "Plume" did not RT this correctly. It did not make a "native retweet". It kinda did like twitter web and changed those "newlines" into "spaces". Plume is not a very good twitter application in this regard. It does not respect the newline, and it is not capable of creating proper "native retweets" when doing so exceeds 140 characters. Good thing, this script is capable of "native retweets" that exceed 140 characters just fine. You can tell by the (3) in the retweet I've made that I was the 3rd person to "native retweet" their tweet. It would've been 4, but that guy using Plume cannot generate native retweets unfortunately. :D

If you wish to disable the newline feature, there is a way, find this setting:

Code: Select all

# Split tweets with newlines into multi-line tweets?
# Without this set to 1, ascii art tweets will not
# show correctly.
# (0 no/1 yes)
set twitter(newline) 1
Change that variable above to 0 and it will no longer span tweets onto multiple lines. Keep in mind with this set to 0, ascii art, and other "special" tweets will not display correct for you. In other words, you wont be able to read tweets like below:
<sp33chy> AntColaninno: ▐─█▐▀█▐▀█▐▀█▐─▌──── ( 91700878507118592@AntColaninno - 4h, 25m, 22s ago via web )
<sp33chy> AntColaninno: ▐▀█▐▀█▐▀▄▐▀▄▐▄▌────
<sp33chy> AntColaninno: ▀─▀▀─▀▀─▀▀─▀─▀─────
<sp33chy> AntColaninno: ▐▀█▐▀█▀█▀▀█▀▐▀▀▐▀█─
<sp33chy> AntColaninno: ▐▀▀▐─█─█──█─▐▀─▐▀▄─
<sp33chy> AntColaninno: ▀──▀▀▀─▀──▀─▀▀▀▀─▀─
Yep, it's that damned movie about witchcraft. Hope this answers your questions. :)
F
Football
Master
Posts: 205
Joined: Fri Dec 26, 2008 3:08 pm
Location: Quakenet, #Football

Post by Football »

Oh I see.

Another question, is it possible that +track can track only up to two words? when I add more then two words it doesn't seem to work, and if I`m looking for a phrase like "Cold Legs" do I +track Cold Legs ?
Idling at #Football, Quakenet.
User avatar
speechles
Revered One
Posts: 1398
Joined: Sat Aug 26, 2006 10:19 pm
Location: emerald triangle, california (coastal redwoods)

Post by speechles »

Football wrote:Oh I see.

Another question, is it possible that +track can track only up to two words? when I add more then two words it doesn't seem to work, and if I`m looking for a phrase like "Cold Legs" do I +track Cold Legs ?
Think of +track like stacking blocks. Each block has a name, and names can be the same. So I want to track: "hello world" combined. Not simply tweets with hello and world somewhere in them, but the two words together "hello word" combined.

To do so is:
+track "hello world" .. The trick is to use double-quotes.

To fully understand tracking, think of it like a list. Which in reality it is. Every word you give the track it will break into words and store those in a list. When it makes queries it turns this list, back into a string. The reason it keeps this list, is to so it can store these in a file in case bot crashes. It then remembers where it left off and can self-resume. To illustrate this point that it is indeed list's we're dealing with after checking indeed it's tracking "hello world" using !track.

Try now:
-track "hello .. this will work. Now type !track again to see exactly what is being tracked. As figure it's simply world" ... With that dangling doube-quote at the end. This is how it's meant to work.

Say I've done +track this that. Afterwards I want a 3rd word. I can add that easily +track 3rdWord to add it without rebuilding the entire track all over again. Issue !track after using +/-track commands and you can see the effect it has on what you are tracking. This lets you see exactly why it isn't working at times.

So when nothing shows up, try !track .. See why it isn't working. If it's wrong, use -track * .. with that * to mean ALL. It will delete all tracked items at once.

Every single operator available here works with tracking. Experiment with it.
Last edited by speechles on Sat Jul 16, 2011 4:50 am, edited 1 time in total.
F
Football
Master
Posts: 205
Joined: Fri Dec 26, 2008 3:08 pm
Location: Quakenet, #Football

Post by Football »

thanks speechless, much more understandable now.

Is the tracking list limited to tracking only two words/phrases?
Idling at #Football, Quakenet.
User avatar
speechles
Revered One
Posts: 1398
Joined: Sat Aug 26, 2006 10:19 pm
Location: emerald triangle, california (coastal redwoods)

Post by speechles »

Football wrote:thanks speechless, much more understandable now.

Is the tracking list limited to tracking only two words/phrases?
No, when you get too much complexity twitter themselves throw an error. The script picks this up and displays it verbatim from twitter. Any errors you see in this regard, for the most part, come directly from twitter themselves. The only time using more than two words can be a problem is when you don't realize you can use OR.

like..

+track this

+track OR that

now my track is this OR that. Since the OR is uppercase, this lets me see tweets with this or that in them. This opens a wider possibility for more than a single pair of words and let's you track multiples as you want.

Anything you can do with !tsearch you can do with !/+/-track. As track is simply automated !tsearch with your tracked query.
<speechles> !tsearch "all your base" +belong -RT +us +all
<sp33chy> ( 4-1 ) Search timelines ( #kangaroopocket@suqmuhnutz ):
<sp33chy> Signorile21: All your base are belong to us. ( 92132359943618560@Signorile21 - 1h, 32m, 59s ago via web )
<sp33chy> Lefter0s: All your base are belong to us.. cc @SomniusX ( 92137530329280512@Lefter0s - 1h, 12m, 27s ago via web )
<sp33chy> SomniusX: All your base are belong to us.. ( 92152142781091840@SomniusX - 14m, 23s ago via Tweetbot for iPhone )
<sp33chy> DavidIzGo: All Your Base Are Belong To Us !!!!!!!!! ( 92153432168861696@DavidIzGo - 9m, 15s ago via web )
Last edited by speechles on Sat Jul 16, 2011 4:58 am, edited 1 time in total.
F
Football
Master
Posts: 205
Joined: Fri Dec 26, 2008 3:08 pm
Location: Quakenet, #Football

Post by Football »

So it will work if I:

+track "Love" OR "Hate" OR "Jealous" OR "Dignity"

?
Idling at #Football, Quakenet.
Post Reply